前段時間有個項目,需要繪制海報,我一聽這不是可以用canvas來實現(xiàn)嗎,所以我在五一假期就記錄下實現(xiàn)方式
我把canvas繪制圖片封裝成一個組件,直接引入使用就好了。
這里雖然是uni-app寫的,但實現(xiàn)方式其實都是一樣的,之后其他原生小程序用到也是大差不大的,真的很簡單??
遇到的坑:uni-app在轉app的時候 - ios 的canvas畫布過大可能導致繪制空白?文章來源:http://www.zghlxwxcb.cn/news/detail-699102.html
創(chuàng)建canvas繪制圖片的組件 - 代碼如下
<template> <view> <canvas canvas-id="canvas" :style="{'width': width + 'px', 'height': height + 'px'}" style="position: fixed; left: -9999px; top: -9999px;"></canvas> </view> </template> <script> export default { name: "drawImage", props: { // 繪制圖片的尺寸 imageSize: { type: Object, default: () => {}, }, // canvas繪制的數(shù)據(jù) canvasData: { type: Array, default: () => [], }, // 是否開始繪制 isDraw: { type: Boolean, default: false, }, }, data() { return { // 屏幕寬度 screenWidth: 0, // canvas畫布的寬度 width: 0, // canvas畫布的高度 height: 0, // 當前圖片放大倍數(shù) - 清晰度 count: 2, }; }, mounted() { // 這段代碼主要是為了防止uni-app在app端IOS的問題,因為在IOS 畫布過大可能會導致繪制空白 - 可以自行調整 // #ifdef APP-PLUS if(uni.getSystemInfoSync().platform === 'ios') { this.count = 1.8; } // #endif }, watch: { // 監(jiān)聽是否開始繪制 isDraw: async function(newVal) { if(newVal) { this.getSystemInfo(); this.getImageByCanvasData(this.canvasData); } } }, methods: { /** 獲取系統(tǒng)信息 */ getSystemInfo() { const { screenWidth } = uni.getSystemInfoSync(); this.width = this.imageSize.width * this.count; this.height = this.imageSize.height * this.count; this.screenWidth = screenWidth; }, /** * 通過數(shù)據(jù)繪制圖片 * @param {array} data canvas繪制的數(shù)組 * 格式:每一項的數(shù)據(jù) * { type: 'rect', attr: { color: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } } * { type: 'image', attr: { image: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } } * { type: 'text', attr: { text: '', x, y, color, size, weight, writingMode } } * */ async getImageByCanvasData(data) { // 獲取canvas上下文對象 const context = uni.createCanvasContext("canvas", this); // 清空畫布 context.clearRect(0, 0, this.width * this.count, this.height * this.count); for(const item of data) { // 判斷類型 if(item.type === 'rect') { // 繪制圓邊矩形 this.drawRoundRectangular( context, item.attr.color, item.attr.x * this.count, item.attr.y * this.count, item.attr.width * this.count, item.attr.height * this.count, item.attr.radian_1 ? item.attr.radian_1 * this.count : 0, item.attr.radian_2 ? item.attr.radian_2 * this.count : -1, item.attr.radian_3 ? item.attr.radian_3 * this.count : -1, item.attr.radian_4 ? item.attr.radian_4 * this.count : -1 ); } else if(item.type === 'image' && item.attr.image) { // 繪制圓邊圖片 await this.drawRoundImageToCanvas( context, item.attr.image, item.attr.x * this.count, item.attr.y * this.count, item.attr.width * this.count, item.attr.height * this.count, item.attr.radian_1 ? item.attr.radian_1 * this.count : 0, item.attr.radian_2 ? item.attr.radian_2 * this.count : -1, item.attr.radian_3 ? item.attr.radian_3 * this.count : -1, item.attr.radian_4 ? item.attr.radian_4 * this.count : -1 ); } else if(item.type === 'text' && item.attr.text) { // 繪制文本 this.drawTextToCanvas( context, item.attr.text, item.attr.x * this.count, item.attr.y * this.count, item.attr.color, parseInt(item.attr.size ? item.attr.size * this.count : 16 * this.count), item.attr.weight, item.attr.writingMode ? item.attr.writingMode : 'initial' ); } } // 繪制圖片 context.draw(false, () => { uni.canvasToTempFilePath({ canvasId: 'canvas', x: 0, y: 0, width: this.width, height: this.height, destWidth: this.width, height: this.height, success: res => { this.$emit("generateImageSuccessful", res.tempFilePath); }, }, this); }); }, /** * 繪制文本 * @param {string} context Canvase的實例 * @param {string} text 文本內容 * @param {number} x 矩形的x坐標 * @param {number} y 矩形的y坐標 * @param {number} color 文本顏色 * @param {number} size 字體的大小 * @param {string} weight 字體的粗細 * @param {string} writingMode 字體的排列方式 - initial 水平 tb 垂直 * */ drawTextToCanvas(context, text, x, y, color = '#000', size = 16, weight = '400', writingMode = 'initial') { context.fillStyle = color; context.font = `normal ${weight} ${size}px sans-serif`; if(writingMode === 'tb') { const temp = text.split(""); for(let i = 0; i < temp.length; i++) { context.fillText(temp[i], x, i * size + y); } } else { // 判斷是否有換行符 const temp = text.split("\n"); for(let i = 0; i < temp.length; i++) { context.fillText(temp[i], x, i * size + y + i * size * 0.2); // i * size * 0.2 增加換行的間距 } } }, /** * 繪制圓邊矩形 * @param {string} context Canvase的實例 * @param {string} color 填充的顏色 * @param {number} x 矩形的x坐標 * @param {number} y 矩形的y坐標 * @param {number} width 矩形的寬度 * @param {number} height 矩形的高度 * @param {number} height 圖片的高度 * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1個參數(shù)代表全部 * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度 * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度 * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度 * */ drawRoundRectangular(context, color, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) { context.save(); this.drawRoundPath(context, x, y, width, height, radian_1, radian_2, radian_3, radian_4); context.setFillStyle(color); context.fill(); context.restore(); }, /** * 繪制圓角圖片 * @param {string} context Canvase的實例 * @param {string} image 圖片地址 * @param {number} x 圖片的x坐標 * @param {number} y 圖片的y坐標 * @param {number} width 圖片的寬度 * @param {number} height 圖片的高度 * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1個參數(shù)代表全部 * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度 * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度 * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度 * */ async drawRoundImageToCanvas(context, image, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) { context.save(); this.drawRoundPath(context, x, y, width, height, radian_1, radian_2, radian_3, radian_4); context.drawImage(await this.handleNetworkImgaeTransferTempImage(image), x, y, width, height); context.restore(); }, /** * 繪制圓邊路徑 * @param {string} context Canvase的實例 * @param {number} x 圖片的x坐標 * @param {number} y 圖片的y坐標 * @param {number} width 圖片的寬度 * @param {number} height 圖片的高度 * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1個參數(shù)代表全部 * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度 * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度 * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度 * */ drawRoundPath(context, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) { // 設置弧度 radian_2 = radian_2 === -1 ? radian_1 : radian_2; radian_3 = radian_3 === -1 ? radian_1 : radian_3; radian_4 = radian_4 === -1 ? radian_1 : radian_4; // 創(chuàng)建路徑 - 繪制帶圓邊的矩形 context.beginPath(); context.moveTo(x + width / 2, y); context.arcTo(x + width, y, x + width, y + height, radian_1); context.arcTo(x + width, y + height, x, y + height, radian_2); context.arcTo(x, y + height, x, y, radian_3); context.arcTo(x, y, x + width, y, radian_4); // 關閉路徑 - 結束繪制 context.closePath(); context.strokeStyle = "transparent"; context.stroke(); context.clip(); }, /** 將網(wǎng)絡圖片變成臨時圖片 */ handleNetworkImgaeTransferTempImage(url) { return new Promise(resolve => { if(url.indexOf('http') === 0) { uni.downloadFile({ url, success: res => { resolve(res.tempFilePath); } }); } else { resolve(url); } }); }, } } </script> <style scoped lang="scss"> </style>
在頁面中的使用?
<template>
<view>
<!-- 顯示一下繪制完成后的路徑 -->
<image :src="tempImage" style="width: 375px; height: 667px;"></image>
<!-- 引入繪制圖片的組件 -->
<drawImage :isDraw="isDraw" :canvasData="canvasData" :imageSize="{width: 375, height: 667}" @generateImageSuccessful="generateImageSuccessful" />
</view>
</template>
<script>
export default {
data() {
return {
// 是否開始繪制
isDraw: false,
/**
* 需要繪制的圖片數(shù)據(jù) - 具體參數(shù)需要看組件內的
* { type: 'rect', attr: { color: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
* { type: 'image', attr: { image: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
* { type: 'text', attr: { text: '', x, y, color, size, weight, writingMode } }
* */
canvasData: [],
// 臨時路徑
tempImage: "",
}
},
onLoad() {
this.canvasData = [
{ type: 'rect', attr: { color: '#00a127', x: 0, y: 0, width: 375, height: 667 } },
{ type: 'text', attr: { text: '略略大魔王', x: 100, y: 100, color: '#fff', size: 20, weight: '500', writingMode: 'tb' } }
];
this.isDraw = true;
},
methods: {
/** 繪制成功后的回調 - 返回一個臨時路徑 */
generateImageSuccessful(image) {
this.tempImage = image;
}
}
};
</script>
<style scoped lang="scss">
</style>
這里只是簡單的演示下(效果圖):
文章來源地址http://www.zghlxwxcb.cn/news/detail-699102.html
到了這里,關于uni-app實現(xiàn)canvas繪制圖片、海報等的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!