最近公司微信公衆號想使用 Apple 式的圓角陰影卡片作文章推薦。這種效果用 Adobe XD 能夠輕鬆作出來,可是無法要求全部編輯都去學習新軟件,因此就打算用前端實現一個小工具。效果以下:html
更新: 已增長 Electron,可打包成 dmg 或 exe 文件運行。詳見 GitHub。前端
功能很簡單,選擇一張圖片,輸入標題文字,下載便可獲得一張 PNG 格式透明背景的圓角陰影卡片圖。核心的步驟有這幾個:git
其中關鍵一步 HTML 轉圖片,利用了著名的 html2canvas 庫,其餘幾步均可以用 Canvas 輕鬆完成。github
爲了簡化功能,對選取的圖片統一按 16:9 居中剪裁。canvas
從 input
得到 File 對象後,把它轉成 Canvas 對象。微信
function listenFileInput() {
const fileInput = document.querySelector(`#${INPUT_FILE_ID}`);
fileInput.addEventListener('change', ev => {
const file = ev.target.files[0];
const image = new Image();
image.onload = loadImage; // 異步過程
image.src = window.URL.createObjectURL(file);
});
}
function loadImage() {
const src = cropImage(this);
document.querySelector(`#${CARD_IMAGE_ID}`).src = src;
}
複製代碼
利用 .drawImage()
將原圖以剪裁模式「繪製」到新的 canvas 上,最後返回 base64 的圖片地址,可直接用於 <img>
標籤的 src
屬性(見上一段代碼末尾)。異步
function cropImage(image) {
const width = image.width;
const height = Math.round(width * TARGET_RATIO);
const cropX = 0;
const cropY = Math.round((image.height - height) / 2);
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, cropX, cropY, width, height, 0, 0, width, height);
return canvas.toDataURL();
}
複製代碼
直接利用 html2canvas
庫。這個庫目前在 GitHub 上有 13K+ star,最新的版本是 1.0.0-alpha.12
,使用很是方便。async
async function generateScreenshot() {
const htmlDom = document.querySelector(`#${HTML_ID}`);
const scale = getScale(); // 由於手機和 PC 的像素比不一樣,HTML 轉圖片時要進行放大,不然在手機上圖片會比較模糊,一般 3 倍左右便可
// HTML 轉 Canvas
const origCanvas = await html2canvas(htmlDom, { scale });
// 生成圓角圖片
const roundCanvas = drawRound(origCanvas, scale);
// 生成陰影效果
return drawShadow(roundCanvas);
}
複製代碼
生成圓角矩形圖片須要用到 Canvas 的 .clip()
方法,其做用是在 canvas 上只顯示 clipping 區域內的內容。思路是先在 canvas 上畫出一個圓角矩形,而後將上一步生成的圖片「貼」進去。工具
因爲通過 clip 的 canvas 只顯示 clipping 區域內的內容,因此不能在這個 canvas 上直接給圖片增長陰影,而是要將該 canvas 繪製到一個更大的 canvas 上,而後給這個圓角矩形的 canvas 增長陰影。學習
function drawRound(origCanvas, scale) {
const roundCanvas = document.createElement('canvas');
roundCanvas.width = DOM_WIDTH * scale;
roundCanvas.height = DOM_HEIGHT * scale;
const roundCtx = roundCanvas.getContext('2d');
const roundRadius = RADIUS * scale;
// 在 canvas 上畫出圓角矩形
const x1 = roundRadius;
const y1 = 0;
const x2 = x1 + roundCanvas.width - 2 * roundRadius;
const y2 = y1;
const x3 = x2 + roundRadius;
const y3 = roundRadius;
const x4 = x3;
const y4 = y3 + roundCanvas.height - 2 * roundRadius;
const x5 = x2;
const y5 = y4 + roundRadius;
const x6 = x1;
const y6 = y5;
const x7 = x6 - roundRadius;
const y7 = y4;
const x8 = x7;
const y8 = y3;
roundCtx.beginPath();
roundCtx.moveTo(x1, y1);
roundCtx.lineTo(x2, y2);
roundCtx.quadraticCurveTo(x3, y2, x3, y3);
roundCtx.lineTo(x4, y4);
roundCtx.quadraticCurveTo(x4, y5, x5, y5);
roundCtx.lineTo(x6, y6);
roundCtx.quadraticCurveTo(x7, y6, x7, y7);
roundCtx.lineTo(x8, y8);
roundCtx.quadraticCurveTo(x8, y1, x1, y1);
// 將圖片「貼」進 clipping 區域,獲得一個圓角矩形的圖片
roundCtx.clip();
roundCtx.drawImage(origCanvas, 0, 0);
return roundCanvas;
}
複製代碼
根據陰影尺寸決定背景 canvas 的尺寸。最終下載的圖片的尺寸,就是背景 canvas 的尺寸。設置過小,陰影會顯示不完整;設置太大,則邊緣留白空間太大,浪費空間且影響使用。
function drawShadow(origCanvas) {
const bgdCanvas = document.createElement('canvas');
bgdCanvas.width = origCanvas.width + MARGIN_WIDTH;
bgdCanvas.height = origCanvas.height + MARGIN_HEIGHT;
const ctx = bgdCanvas.getContext('2d');
ctx.shadowOffsetX = SHADOW_X;
ctx.shadowOffsetY = SHADOW_Y;
ctx.shadowBlur = SHADOW_BLUR;
ctx.shadowColor = SHADOW_COLOR;
ctx.drawImage(origCanvas, MARGIN_WIDTH / 2, 0);
return bgdCanvas;
}
複製代碼
將作好的帶有陰影的 canvas,經過 .toDataURL()
獲得 base64 地址,設爲 <a href="" download />
的 href
屬性,便可下載使用。
小工具的完整代碼位於 GitHub:reading-card-generator。