uniapp 封裝 canvas
阿新 • • 發佈:2020-07-28
簡單的對一些常用的canvas api進行了封裝
支援功能:
畫線性漸變(支援多顏色)
畫圖片(支援自動裁切圓角)
畫文字(支援自動換行、省略)
畫圓形
畫矩形(支援圓角)
import * as config from '@/config/index.js'; class MDCanvas { /** * 建構函式 * @param {Object} ctx canvas的例項 * @param {String} canvasId canvas元件的canvasId * @param {Array} canvasData 要畫canvas的陣列*/ constructor(ctx, canvasId, canvasData) { this._canvasData = canvasData; // 要畫的canvas陣列 this._ctx = ctx; // canvas 例項 this._canvasId = canvasId; // canvasId this._pmImgTask = []; // promise下載圖片任務 } /** * 畫canvas */ drawCanvas() { uni.showToast({ title: '載入素材..', icon:'loading', mask: true, duration: 10 * 1000 }); const ctx = this._ctx; const canvasId = this._canvasId; this.asyncImage(); return new Promise((resolve, reject) => { Promise.all(this._pmImgTask).then(() => { this._canvasData.forEach(item => {switch (item.type) { case 'lg': // 線性漸變 { const lrd = ctx.createLinearGradient(uni.upx2px(item.x0), uni.upx2px(item.y0), uni.upx2px(item.x1), uni.upx2px(item.y1)); item.colors.forEach(x => { lrd.addColorStop(x.scope, x.value); }); ctx.setFillStyle(lrd); ctx.fillRect(uni.upx2px(item.x), uni.upx2px(item.y), uni.upx2px(item.width), uni.upx2px(item.height)); } break; case 'img': // 圖片 ctx.save(); ctx.beginPath(); if (item.border === 'circle') { // 圓角 const radius = item.width <= item.height ? item.width : item.height; if (item.width >= item.height) { item.x = item.x - (item.width = item.height) / 2; } else { item.y = item.y - (item.height - item.width) / 2; } ctx.arc(uni.upx2px(item.x + item.width / 2), uni.upx2px(item.y + item.height / 2), uni.upx2px(radius / 2), 0, 2 * Math.PI); ctx.setFillStyle('#fff'); ctx.fill(); ctx.clip(); } ctx.drawImage(item.content, uni.upx2px(item.x), uni.upx2px(item.y), uni.upx2px(item.width), uni.upx2px( item.height)); ctx.restore(); break; case 'text': // 文字 { ctx.setFillStyle(item.color); if (item.textAlign) ctx.setTextAlign(item.textAlign); if (item.baseLine) ctx.setTextBaseline(item.baseLine); const bold = item.bold ? item.bold : 'normal'; // 加粗 const family = item.family ? item.family : 'Microsoft YaHei'; // 字型 ctx.font = `${bold} ${Math.floor(uni.upx2px(item.fontSize))}px ${family}`; const arr = this.newLine(item.content, uni.upx2px(item.width), item.row); // 換行 const lineHeight = item.lineHeight ? item.lineHeight : Number(item.fontSize || 30) * 1.2; // 字型大小預設30 arr.forEach((itemText, key) => { ctx.fillText(itemText, uni.upx2px(item.x), uni.upx2px(item.y + lineHeight * key)); }); } break; case 'shape': // 形狀 ctx.save(); ctx.beginPath(); if (item.linearGradient) { // 漸變 const grad = ctx.createLinearGradient(item.linearGradient.x1, item.linearGradient.y1, item.linearGradient .x2, item.linearGradient.y2); // 建立一個漸變色線性物件 grad.addColorStop(0, item.linearGradient.color1); // 定義漸變色顏色 grad.addColorStop(1, item.linearGradient.color2); ctx.setFillStyle(grad); } else { ctx.setFillStyle(item.background); } switch (item.shape) { case 'circle': // 圓圈 ctx.arc(uni.upx2px(item.x), uni.upx2px(item.y), uni.upx2px(item.radius), 0, 2 * Math.PI); ctx.fill(); break; case 'rect': // 四變形 ctx.fillRect(uni.upx2px(item.x), uni.upx2px(item.y), uni.upx2px(item.width), uni.upx2px( item.height)); break; case 'round': // 帶圓角的形狀 { const radius = item.radius ? uni.upx2px(item.radius) : 4; ctx.arc(uni.upx2px(item.x) + radius, uni.upx2px(item.y) + radius, radius, Math.PI, ( Math.PI * 3) / 2); ctx.lineTo(uni.upx2px(item.width) - radius + uni.upx2px(item.x), uni.upx2px(item.y)); ctx.arc(uni.upx2px(item.width) - radius + uni.upx2px(item.x), radius + uni.upx2px( item.y), radius, (Math.PI * 3) / 2, Math.PI * 2); ctx.lineTo(uni.upx2px(item.width) + uni.upx2px(item.x), uni.upx2px(item.height) + uni .upx2px( item.y) - radius); ctx.arc( uni.upx2px(item.width) - radius + uni.upx2px(item.x), uni.upx2px(item.height) - radius + uni.upx2px(item.y), radius, 0, (Math.PI * 1) / 2 ); ctx.lineTo(radius + uni.upx2px(item.x), uni.upx2px(item.height) + uni.upx2px(item.y)); ctx.arc(radius + uni.upx2px(item.x), uni.upx2px(item.height) - radius + uni.upx2px( item.y), radius, (Math.PI * 1) / 2, Math.PI); ctx.closePath(); ctx.fill(); ctx.strokeStyle = item.background; ctx.stroke(); } break; } ctx.restore(); break; } }); uni.showToast({ title: '正在生成..', icon: 'loading', mask: true, duration: 2 * 1000 }); ctx.draw(false, () => { setTimeout(() => { uni.canvasToTempFilePath({ canvasId: canvasId, success: res => { resolve(res.tempFilePath); }, fail: err => { reject(err); }, complete() { uni.hideToast(); } }); }, 300); }); }); }); } /** * 計算換行 * @param {String} str 文字 * @param {Number} width 寬度 * @param {Number} row 行數 */ newLine(str, width, row) { const arr = []; let str1 = ''; let newArr = []; const ctx = this._ctx; if (width) { for (let i = 0; i < str.length; i++) { if (i === str.length - 1) { const str2 = str1 + str[i]; if (this.measureText(ctx.state.fontSize, str2)) { arr.push(str2); } else { arr.push(str1); str1 = str[i]; arr.push(str1); } } else { const str2 = str1 + str[i]; if (this.measureText(ctx.state.fontSize, str2) > width) { arr.push(str1); str1 = str[i]; } else { str1 = str2; } } } } else { arr.push(str); } newArr = row ? arr.slice(0, row) : arr; if (row && arr.length > row) { const len = newArr[row - 1].length; const lastStr = newArr[row - 1][len - 1]; const pattern = new RegExp('[\u4E00-\u9FA5]+'); newArr[row - 1] = pattern.test(lastStr) ? newArr[row - 1].substring(0, newArr[row - 1].length - 1) + '...' : newArr[row - 1].substring(0, newArr[row - 1].length - 2) + '...'; } return newArr; } /** * 計算文字寬度 * @param {Number} fontSize * @param {String} str */ measureText(fontSize, str) { if (!str) return 0; if (typeof str !== 'string') { str += ''; } return (str.replace(/[^\x00-\xff]/g, 'ab').length / 2) * fontSize; } /** * 處理圖片 */ asyncImage() { this._canvasData.forEach(x => { if (x.type === 'img') { const p = this.downLoadFile(x.content, x.useGet).then(res => { x.content = res; }); this._pmImgTask.push(p); } }); } /** * 下載檔案 * @param {String} url 下載檔案地址 * @param {Boolean} useGet 是否需要從伺服器下載(小程式未配置下載域名、跨域時需要伺服器支援) */ downLoadFile(url, useGet) { if (url.indexOf('base64') > -1) { // base64檔案直接返回 return new Promise(resolve => { resolve(url); }); } if (url.indexOf('qlogo.cn') > -1 || useGet) { // 微信頭像從伺服器獲取 url = `${config.interfaceUrl}/common/img/wechat/avatar?urlHttp=${encodeURIComponent(url)}`; } else { url += `?r=${+new Date()}`; // 加上隨機數防止微信快取導致canvas畫圖時出錯 } return new Promise((resolve, reject) => { uni.downloadFile({ url, success: res => { resolve(res.tempFilePath); }, fail: err => { console.error('下載檔案出錯', err, url); reject(err); uni.showModal({ title: '提示', showCancel: false, content: err.errMsg }); } }); }); } } module.exports = MDCanvas;
config 檔案可自行建立進行引用或者將伺服器請求路徑寫死可以不必引用該檔案
使用demo:
<template> <view class="container"><canvas class="canvas" canvas-id="canvasId"></canvas></view> </template> <script> import MDCanvas from '@/common/md-canvas.js'; export default { onReady() { // 建立canvas例項 const ctx = uni.createCanvasContext('canvasId', this); // 畫布步驟陣列 const canvasData = [ { type: 'lg', // 畫線性漸變 x0: 0, // 漸變起點的 x 座標 y0: 0, // 漸變起點的 y 座標 x1: 0, // 漸變終點的 x 座標 y0: 750, // 漸變終點的 y 座標 x: 0, // 右上角橫座標 y: 0, // 右上角縱座標 width: 750, // 寬 height: 750, // 高 colors: [ // 漸變顏色 { scope: 0, // 表示漸變中開始與結束之間的位置,範圍 0-1 value: '#fff' // 色值 }, { scope: 0.5, value: '#f00' }, { scope: 1, value: '#fff' }, ] }, { type: 'img', // 畫圖片 border: 'circle', // border的值為circle時 自動裁切成圓形 content: `https://f.modo5.com/third/wxminiprogram/oppein/share_poster_seckill_bg.png`, // 圖片Url地址 x: 18, // 右上角橫座標 y: 0,// 右上角縱座標 width: 714, // 寬 height: 736 // 高 }, { type: 'text', // 畫文字 content: 'This is text.', // 文字內容 color: '#fff', // 文字顏色 x: 120, // 右上角橫座標 y: 120, // 右上角縱座標 width: 180, // 文字佔用寬 fontSize: 30, // 字型大小 row: 1 // 字型佔用行數 }, { type: 'shape', // 畫形狀 background: '#fff', // 背景顏色 shape: 'circle', // 指定形狀為圓形 x: 150, // 右上角橫座標 y: 150, // 右上角縱座標 radius: 10 // 半徑 }, { type: 'shape', // 畫形狀 background: '#ccc', // 背景顏色 shape: 'rect', // 指定形狀為矩形 x: 200, // 右上角橫座標 y: 150, // 右上角縱座標 width: 20, // 寬 height: 20 // 高 }, { type: 'shape', // 畫形狀 background: '#3fd', // 背景顏色 shape: 'round', // 指定形狀為帶圓角的矩形 x: 250, // 右上角橫座標 y: 150, // 右上角縱座標 width: 40, // 寬 height: 40, // 高 radius: 10 // 圓角值 } ]; // 初始化 const mdCtx = new MDCanvas(ctx, 'canvasId', canvasData); // 畫矩形 mdCtx .drawCanvas() .then(res => { console.log("臨時圖片地址",res); }) .catch(err => { console.error(err); }); } }; </script> <style lang="scss"> .container { display: flex; align-items: center; justify-content: center; height: 100vh; width: 750rpx; } .canvas { width: 750rpx; height: 750rpx; } </style>
實現效果: