• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

uni-app实现canvas绘制图片、海报等

武飞扬头像
略略大魔王丶
帮助1

前段时间有个项目,需要绘制海报,我一听这不是可以用canvas来实现吗,所以我在五一假期就记录下实现方式

我把canvas绘制图片封装成一个组件,直接引入使用就好了。

这里虽然是uni-app写的,但实现方式其实都是一样的,之后其他原生小程序用到也是大差不大的,真的很简单😆

遇到的坑:uni-app在转app的时候 - ios 的canvas画布过大可能导致绘制空白 

创建canvas绘制图片的组件 - 代码如下

  1.  
    <template>
  2.  
    <view>
  3.  
    <canvas canvas-id="canvas" :style="{'width': width 'px', 'height': height 'px'}" style="position: fixed; left: -9999px; top: -9999px;"></canvas>
  4.  
    </view>
  5.  
    </template>
  6.  
     
  7.  
    <script>
  8.  
    export default {
  9.  
    name: "drawImage",
  10.  
    props: {
  11.  
    // 绘制图片的尺寸
  12.  
    imageSize: {
  13.  
    type: Object,
  14.  
    default: () => {},
  15.  
    },
  16.  
    // canvas绘制的数据
  17.  
    canvasData: {
  18.  
    type: Array,
  19.  
    default: () => [],
  20.  
    },
  21.  
    // 是否开始绘制
  22.  
    isDraw: {
  23.  
    type: Boolean,
  24.  
    default: false,
  25.  
    },
  26.  
    },
  27.  
    data() {
  28.  
    return {
  29.  
    // 屏幕宽度
  30.  
    screenWidth: 0,
  31.  
    // canvas画布的宽度
  32.  
    width: 0,
  33.  
    // canvas画布的高度
  34.  
    height: 0,
  35.  
    // 当前图片放大倍数 - 清晰度
  36.  
    count: 2,
  37.  
    };
  38.  
    },
  39.  
    mounted() {
  40.  
    // 这段代码主要是为了防止uni-app在app端IOS的问题,因为在IOS 画布过大可能会导致绘制空白 - 可以自行调整
  41.  
    // #ifdef APP-PLUS
  42.  
    if(uni.getSystemInfoSync().platform === 'ios') {
  43.  
    this.count = 1.8;
  44.  
    }
  45.  
    // #endif
  46.  
    },
  47.  
    watch: {
  48.  
    // 监听是否开始绘制
  49.  
    isDraw: async function(newVal) {
  50.  
    if(newVal) {
  51.  
    this.getSystemInfo();
  52.  
    this.getImageByCanvasData(this.canvasData);
  53.  
    }
  54.  
    }
  55.  
    },
  56.  
    methods: {
  57.  
    /** 获取系统信息 */
  58.  
    getSystemInfo() {
  59.  
    const { screenWidth } = uni.getSystemInfoSync();
  60.  
     
  61.  
    this.width = this.imageSize.width * this.count;
  62.  
    this.height = this.imageSize.height * this.count;
  63.  
     
  64.  
    this.screenWidth = screenWidth;
  65.  
    },
  66.  
     
  67.  
    /**
  68.  
    * 通过数据绘制图片
  69.  
    * @param {array} data canvas绘制的数组
  70.  
    * 格式:每一项的数据
  71.  
    * { type: 'rect', attr: { color: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
  72.  
    * { type: 'image', attr: { image: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
  73.  
    * { type: 'text', attr: { text: '', x, y, color, size, weight, writingMode } }
  74.  
    * */
  75.  
    async getImageByCanvasData(data) {
  76.  
    // 获取canvas上下文对象
  77.  
    const context = uni.createCanvasContext("canvas", this);
  78.  
    // 清空画布
  79.  
    context.clearRect(0, 0, this.width * this.count, this.height * this.count);
  80.  
     
  81.  
    for(const item of data) {
  82.  
    // 判断类型
  83.  
    if(item.type === 'rect') {
  84.  
    // 绘制圆边矩形
  85.  
    this.drawRoundRectangular(
  86.  
    context,
  87.  
    item.attr.color,
  88.  
    item.attr.x * this.count,
  89.  
    item.attr.y * this.count,
  90.  
    item.attr.width * this.count,
  91.  
    item.attr.height * this.count,
  92.  
    item.attr.radian_1 ? item.attr.radian_1 * this.count : 0,
  93.  
    item.attr.radian_2 ? item.attr.radian_2 * this.count : -1,
  94.  
    item.attr.radian_3 ? item.attr.radian_3 * this.count : -1,
  95.  
    item.attr.radian_4 ? item.attr.radian_4 * this.count : -1
  96.  
    );
  97.  
    }
  98.  
    else if(item.type === 'image' && item.attr.image) {
  99.  
    // 绘制圆边图片
  100.  
    await this.drawRoundImageToCanvas(
  101.  
    context,
  102.  
    item.attr.image,
  103.  
    item.attr.x * this.count,
  104.  
    item.attr.y * this.count,
  105.  
    item.attr.width * this.count,
  106.  
    item.attr.height * this.count,
  107.  
    item.attr.radian_1 ? item.attr.radian_1 * this.count : 0,
  108.  
    item.attr.radian_2 ? item.attr.radian_2 * this.count : -1,
  109.  
    item.attr.radian_3 ? item.attr.radian_3 * this.count : -1,
  110.  
    item.attr.radian_4 ? item.attr.radian_4 * this.count : -1
  111.  
    );
  112.  
    }
  113.  
    else if(item.type === 'text' && item.attr.text) {
  114.  
    // 绘制文本
  115.  
    this.drawTextToCanvas(
  116.  
    context,
  117.  
    item.attr.text,
  118.  
    item.attr.x * this.count,
  119.  
    item.attr.y * this.count,
  120.  
    item.attr.color,
  121.  
    parseInt(item.attr.size ? item.attr.size * this.count : 16 * this.count),
  122.  
    item.attr.weight,
  123.  
    item.attr.writingMode ? item.attr.writingMode : 'initial'
  124.  
    );
  125.  
    }
  126.  
    }
  127.  
     
  128.  
    // 绘制图片
  129.  
    context.draw(false, () => {
  130.  
    uni.canvasToTempFilePath({
  131.  
    canvasId: 'canvas',
  132.  
    x: 0,
  133.  
    y: 0,
  134.  
    width: this.width,
  135.  
    height: this.height,
  136.  
    destWidth: this.width,
  137.  
    height: this.height,
  138.  
    success: res => {
  139.  
    this.$emit("generateImageSuccessful", res.tempFilePath);
  140.  
    },
  141.  
    }, this);
  142.  
    });
  143.  
    },
  144.  
     
  145.  
    /**
  146.  
    * 绘制文本
  147.  
    * @param {string} context Canvase的实例
  148.  
    * @param {string} text 文本内容
  149.  
    * @param {number} x 矩形的x坐标
  150.  
    * @param {number} y 矩形的y坐标
  151.  
    * @param {number} color 文本颜色
  152.  
    * @param {number} size 字体的大小
  153.  
    * @param {string} weight 字体的粗细
  154.  
    * @param {string} writingMode 字体的排列方式 - initial 水平 tb 垂直
  155.  
    * */
  156.  
    drawTextToCanvas(context, text, x, y, color = '#000', size = 16, weight = '400', writingMode = 'initial') {
  157.  
    context.fillStyle = color;
  158.  
    context.font = `normal ${weight} ${size}px sans-serif`;
  159.  
    if(writingMode === 'tb') {
  160.  
    const temp = text.split("");
  161.  
    for(let i = 0; i < temp.length; i ) {
  162.  
    context.fillText(temp[i], x, i * size y);
  163.  
    }
  164.  
    }
  165.  
    else {
  166.  
    // 判断是否有换行符
  167.  
    const temp = text.split("\n");
  168.  
    for(let i = 0; i < temp.length; i ) {
  169.  
    context.fillText(temp[i], x, i * size y i * size * 0.2); // i * size * 0.2 增加换行的间距
  170.  
    }
  171.  
    }
  172.  
    },
  173.  
     
  174.  
    /**
  175.  
    * 绘制圆边矩形
  176.  
    * @param {string} context Canvase的实例
  177.  
    * @param {string} color 填充的颜色
  178.  
    * @param {number} x 矩形的x坐标
  179.  
    * @param {number} y 矩形的y坐标
  180.  
    * @param {number} width 矩形的宽度
  181.  
    * @param {number} height 矩形的高度
  182.  
    * @param {number} height 图片的高度
  183.  
    * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1个参数代表全部
  184.  
    * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度
  185.  
    * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度
  186.  
    * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度
  187.  
    * */
  188.  
    drawRoundRectangular(context, color, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) {
  189.  
    context.save();
  190.  
    this.drawRoundPath(context, x, y, width, height, radian_1, radian_2, radian_3, radian_4);
  191.  
    context.setFillStyle(color);
  192.  
    context.fill();
  193.  
    context.restore();
  194.  
    },
  195.  
     
  196.  
    /**
  197.  
    * 绘制圆角图片
  198.  
    * @param {string} context Canvase的实例
  199.  
    * @param {string} image 图片地址
  200.  
    * @param {number} x 图片的x坐标
  201.  
    * @param {number} y 图片的y坐标
  202.  
    * @param {number} width 图片的宽度
  203.  
    * @param {number} height 图片的高度
  204.  
    * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1个参数代表全部
  205.  
    * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度
  206.  
    * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度
  207.  
    * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度
  208.  
    * */
  209.  
    async drawRoundImageToCanvas(context, image, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) {
  210.  
    context.save();
  211.  
    this.drawRoundPath(context, x, y, width, height, radian_1, radian_2, radian_3, radian_4);
  212.  
    context.drawImage(await this.handleNetworkImgaeTransferTempImage(image), x, y, width, height);
  213.  
    context.restore();
  214.  
    },
  215.  
     
  216.  
    /**
  217.  
    * 绘制圆边路径
  218.  
    * @param {string} context Canvase的实例
  219.  
    * @param {number} x 图片的x坐标
  220.  
    * @param {number} y 图片的y坐标
  221.  
    * @param {number} width 图片的宽度
  222.  
    * @param {number} height 图片的高度
  223.  
    * @param {number} radian_1 弧度大小 - radian_1 右上 的弧度, 1个参数代表全部
  224.  
    * @param {number} radian_2 弧度大小 - radian_2 右下 的弧度
  225.  
    * @param {number} radian_3 弧度大小 - radian_3 左下 的弧度
  226.  
    * @param {number} radian_4 弧度大小 - radian_4 左上 的弧度
  227.  
    * */
  228.  
    drawRoundPath(context, x, y, width, height, radian_1 = 0, radian_2 = -1, radian_3 = -1, radian_4 = -1) {
  229.  
    // 设置弧度
  230.  
    radian_2 = radian_2 === -1 ? radian_1 : radian_2;
  231.  
    radian_3 = radian_3 === -1 ? radian_1 : radian_3;
  232.  
    radian_4 = radian_4 === -1 ? radian_1 : radian_4;
  233.  
     
  234.  
    // 创建路径 - 绘制带圆边的矩形
  235.  
    context.beginPath();
  236.  
    context.moveTo(x width / 2, y);
  237.  
    context.arcTo(x width, y, x width, y height, radian_1);
  238.  
    context.arcTo(x width, y height, x, y height, radian_2);
  239.  
    context.arcTo(x, y height, x, y, radian_3);
  240.  
    context.arcTo(x, y, x width, y, radian_4);
  241.  
    // 关闭路径 - 结束绘制
  242.  
    context.closePath();
  243.  
    context.strokeStyle = "transparent";
  244.  
    context.stroke();
  245.  
    context.clip();
  246.  
    },
  247.  
     
  248.  
    /** 将网络图片变成临时图片 */
  249.  
    handleNetworkImgaeTransferTempImage(url) {
  250.  
    return new Promise(resolve => {
  251.  
    if(url.indexOf('http') === 0) {
  252.  
    uni.downloadFile({
  253.  
    url,
  254.  
    success: res => {
  255.  
    resolve(res.tempFilePath);
  256.  
    }
  257.  
    });
  258.  
    }
  259.  
    else {
  260.  
    resolve(url);
  261.  
    }
  262.  
    });
  263.  
    },
  264.  
    }
  265.  
    }
  266.  
    </script>
  267.  
     
  268.  
    <style scoped lang="scss">
  269.  
     
  270.  
    </style>
学新通

在页面中的使用 

  1.  
    <template>
  2.  
    <view>
  3.  
    <!-- 显示一下绘制完成后的路径 -->
  4.  
    <image :src="tempImage" style="width: 375px; height: 667px;"></image>
  5.  
     
  6.  
    <!-- 引入绘制图片的组件 -->
  7.  
    <drawImage :isDraw="isDraw" :canvasData="canvasData" :imageSize="{width: 375, height: 667}" @generateImageSuccessful="generateImageSuccessful" />
  8.  
    </view>
  9.  
    </template>
  10.  
     
  11.  
    <script>
  12.  
    export default {
  13.  
    data() {
  14.  
    return {
  15.  
    // 是否开始绘制
  16.  
    isDraw: false,
  17.  
    /**
  18.  
    * 需要绘制的图片数据 - 具体参数需要看组件内的
  19.  
    * { type: 'rect', attr: { color: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
  20.  
    * { type: 'image', attr: { image: '', x, y, width, height, radian_1, radian_2, radian_3, radian_4 } }
  21.  
    * { type: 'text', attr: { text: '', x, y, color, size, weight, writingMode } }
  22.  
    * */
  23.  
    canvasData: [],
  24.  
    // 临时路径
  25.  
    tempImage: "",
  26.  
    }
  27.  
    },
  28.  
    onLoad() {
  29.  
    this.canvasData = [
  30.  
    { type: 'rect', attr: { color: '#00a127', x: 0, y: 0, width: 375, height: 667 } },
  31.  
    { type: 'text', attr: { text: '略略大魔王', x: 100, y: 100, color: '#fff', size: 20, weight: '500', writingMode: 'tb' } }
  32.  
    ];
  33.  
     
  34.  
    this.isDraw = true;
  35.  
    },
  36.  
    methods: {
  37.  
    /** 绘制成功后的回调 - 返回一个临时路径 */
  38.  
    generateImageSuccessful(image) {
  39.  
    this.tempImage = image;
  40.  
    }
  41.  
    }
  42.  
    };
  43.  
    </script>
  44.  
     
  45.  
    <style scoped lang="scss">
  46.  
     
  47.  
    </style>
学新通

这里只是简单的演示下(效果图):

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfaaea
系列文章
更多 icon
同类精品
更多 icon
继续加载