微信小程序微信小程序开发微信小程序开发

微信小程序分享图片的简易canvas工具类

2018-11-01  本文已影响11人  韦弦Zhy
在微信小程序中生成一张海报以便用户可以分享到朋友圈的功能在很多微信小程序中都有,今天分享一个自己写的简易canvas工具类

如有需要请自取:GitHub微信小程序保存图片分享的 canvas 简易自用工具类

demo.jpg
所有参数按顺序传,没有默认值的必须传,如果需要修改最后一个参数,所有参数都需要传。。。(js小白,不知道怎么解决)

下面将大部分功能贴在下方

首先,一个将rpx转成px的方法,canvas使用px作为单位,(我这里UI使用750px宽做设计图,使用的时候将标注软件的px改成rpx就是我们需要的)这个方法我直接写在app.js里面,因为用到的地方很多,比如还有OSS
  onLaunch: function () {
    wx.getSystemInfo({
      success: res => {
        //获取屏幕宽度,然后根据宽度图获取图片
        this.globalData.screenWidth = res.screenWidth;
        this.globalData.pixelRatio = res.pixelRatio;
        this.globalData.factor = res.screenWidth / 750;
      },
    })
  },

  globalData: {
    userInfo: null,
    factor: 0.5,
    screenWidth: 375,
    pixelRatio: 2
  },
 
  toPx: function (rpx) {
    //这里的 2 可以改成 动态的 pixelRatio
    //设成固定的是为了在不同设备生成的图片可以在同一级分辨率 
    return rpx * this.globalData.factor * 2;
  }

1、绘制纯色背景

最开始没有写这个,但是安卓真机上需要所以加上了,
这个其实就是画一个矩形,记得宽高要超过你需要绘制的宽高

/**
 * @author 赵勇
 * @desc canvas 画纯色背景 (在iOS上不画默认白色,安卓上不画默认是透明色)
 * 
 *  @param ctx canvas上下文
 *  @param color 背景颜色
 *  @param width  背景宽
 *  @param height 背景高
 *  @param x 轴 坐标
 *  @param y 轴 坐标
 *
 */
function drawBackground(ctx, color = 'white', width = 1000, height = 3000, x = 0, y = 0) {
    ctx.rect(x, y, width, height)
    ctx.setFillStyle(color)
    ctx.fill()
}
使用

如果默认值满足,也可以只传一个参数即:canvas的上下文

canvasUtil.drawBackground(ctx);

要修改颜色可以

canvasUtil.drawBackground(ctx, '#999999');
2、绘制文字

这个是为了方便设置字体,字号,同时返回了最终的x轴坐标,是为了适应某些需要拼接文字的需求(比如价格拼接单位,但是单位字号不一样)

/**
 * @author 赵勇
 * @desc canvas 画文字
 * 
 *  @param ctx canvas上下文
 *  @param text 需要绘制的文字文字
 *  @param x 轴 坐标
 *  @param y 轴 坐标
 *  @param size 字体大小
 *  @param align 字体对齐方式
 *  @param color 字体颜色
 *  @param baseline 字体基线对齐方式
 *  @param fontWeight 字体粗细
 *
 *  @return 绘制完的 x轴 坐标值
 */
function drawText(ctx, text, x, y, size = '18', align = 'left', color = '#333333', baseline = 'top', fontWeight = 'normal') {
    size = parseInt(size)
    let font = `${fontWeight} ${size}px sans-serif`;
    ctx.font = font;

    ctx.setTextAlign(align);
    ctx.setFillStyle(color);
    ctx.setTextBaseline(baseline);
    ctx.fillText(text, x, y);
    let m = ctx.measureText("" + text)
    return m.width + x;
}
注意点:

(1)size 转成整数是因为字号设置是用rpx转px的,会出现小数的情况,而在canvas里面。。。一不小心就报错了
(2)ctx.measureText();可以测量字符串的长度,但是不能是数字、、、我这里就踩坑了,所以转成字符串

使用:

只传部分参数

canvasUtil.drawBreakText(ctx, "text", app.toPx(50), app.toPx(350));

所有参数都用上

  canvasUtil.drawBreakText(ctx, "text", app.toPx(50), app.toPx(350), app.toPx(38), app.toPx(40), app.toPx(650), 'left', '#999999', 'top', 'bold');
3、绘制换行文字

这个需求也算是常见了吧,这里将返回值改成最后的y坐标值,相信大家也是能理解的啦

/**
 * @author  赵勇
 * @desc  canvas画文字带换行 单位(px)
 * 
 * @param ctx canvas上下文
 * @param text 需要绘制的文字文字
 * @param x x轴 坐标值    
 * @param y y轴 坐标值
 * @param lineHeight 行高
 * @param maxW  文字最宽值
 * @param align 文字对齐方式
 * @param color 字体颜色
 * @param baseline 字体基线对齐方式
 * @param fontWeight 字体粗细
 *
 * @return 最后一行的底部 y轴 坐标值
 */
function drawBreakText(ctx, text, x, y, size, lineHeight, maxW, align = 'left', color = '#333333', baseline = 'top', fontWeight = 'normal') {
    size = parseInt(size)
    let font = `${fontWeight} ${size}px sans-serif`;
    ctx.font = font;
    ctx.setTextAlign(align);
    ctx.setFillStyle(color);
    ctx.setTextBaseline(baseline);

    let textArr = text.split("");
    const count = textArr.length;
    let tempText = "";
    let row = [];
    for (let i = 0; i < count; i++) {
        if (ctx.measureText(tempText).width < maxW) {
            tempText += textArr[i];
        } else {
            I--;
            row.push(tempText);
            tempText = "";
        }
    }
    row.push(tempText);

    let i = 0;
    for (i; i < row.length; i++) {
        ctx.fillText(row[i], x, y + i * lineHeight, maxW);
    }

    return y + i * lineHeight;
}

具体实现逻辑就是循环所有字符,然后去判断长度,看的不是很明白的可以cue我

4、绘制图片,但是保持比例填充

我想在小程序里面,只有mode:aspectFill才是我们真的用的最多的吧!

/**
 * @author 赵勇
 * @desc canvas 绘制图  mode:aspectFill 保持比例填充
 *
 * @param ctx canvas上下文
 * @param imagePath 图片url
 * @param sWidth 原图宽
 * @param sHeight 原图高
 * @param dx canvas x轴 坐标
 * @param dy canvas y轴 坐标
 * @param dWidth canvas 宽
 * @param dHeight canvas 高
 */
function drawImageAspectFill(ctx, imagePath, sWidth, sHeight, dx, dy, dWidth, dHeight) {
    //canvas与图片宽高比
    var wRatio = dWidth / sWidth;
    var hRatio = dHeight / sHeight;
    //裁剪图片中间部分
    if (sWidth >= dWidth && sHeight >= dHeight || sWidth <= dWidth && sHeight <= dHeight) {
        if (wRatio > hRatio) {
            ctx.drawImage(imagePath, 0, (sHeight - dHeight / wRatio) / 2, sWidth, dHeight / wRatio, dx, dy, dWidth, dHeight);
        } else {
            ctx.drawImage(imagePath, (sWidth - dWidth / hRatio) / 2, 0, dWidth / hRatio, sHeight, dx, dy, dWidth, dHeight);
        }
    }
    //拉伸图片
    else {
        if (sWidth <= dWidth) {
            ctx.drawImage(imagePath, 0, (sHeight - dHeight / wRatio) / 2, sWidth, dHeight / wRatio, dx, dy, dWidth, dHeight);
        } else {
            ctx.drawImage(imagePath, (sWidth - dWidth / hRatio) / 2, 0, dWidth / hRatio, sHeight, dx, dy, dWidth, dHeight);
        }
    }
}

这个具体看例子吧。。我懒得写了

5、绘制图片,但是保持比例填充,同时还要圆角

好像在安卓上会有bug。。。一脸尴尬。。。
(1)绘制圆角的方法,网上很多,我抄的,所以也就不做解释了


/**
 * @author 赵勇
 * @desc canvas 绘制圆角视图
 *
 * @param ctx canvas上下文
 * @param x canvas x轴 坐标
 * @param y canvas y轴 坐标
 * @param w 宽
 * @param h 高
 * @param r 圆角半径
 * @param fill 填充 true  边框 false
 */
function drawRoundRect(ctx, x, y, w, h, r, fill = true) {
    //开始绘制
    ctx.beginPath();
    // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
    // 这里是使用 fill 还是 stroke都可以,二选一即可
    if (fill) {
        ctx.setFillStyle('transparent')
    } else {
        ctx.setStrokeStyle('transparent')
    }

    // 左上角
    ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)

    // border-top
    ctx.lineTo(x + w - r, y)

    // 右上角
    ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)

    // border-right
    ctx.lineTo(x + w, y + h - r)

    // 右下角
    ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)

    // border-bottom
    ctx.lineTo(x + r, y + h)

    // 左下角
    ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)

    // border-left
    ctx.lineTo(x, y + r)

    // 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应
    if (fill) {
        ctx.fill()
    } else {
        ctx.stroke()
    }

    ctx.closePath()
}

(2)最后调用绘制圆角和绘制图片的方法

/**
 * @author 赵勇
 * @desc canvas 绘制圆角图  mode:aspectFill 保持比例填充
 *
 * @param ctx canvas上下文
 * @param imagePath 图片url
 * @param sWidth 原图宽
 * @param sHeight 原图高
 * @param dx canvas x轴 坐标
 * @param dy canvas y轴 坐标
 * @param dWidth canvas 宽
 * @param dHeight canvas 高
 * @param radius 圆角半径
 */
function drawImageAspectFillWidthCorner(ctx, imagePath, sWidth, sHeight, dx, dy, dWidth, dHeight, radius = 0) {
    ctx.save();
    drawRoundRect(ctx, dx, dy, dWidth, dHeight, radius);
    // 剪切
    ctx.clip()
    drawImageAspectFill(ctx, imagePath, sWidth, sHeight, dx, dy, dWidth, dHeight);
    ctx.restore();
}

这里需要注意的就是这三个方法的调用时机啦

    ctx.save();
    ctx.clip()
    ctx.restore();
最后一个,下载图片。。。what?下载图片和canvas有啥关系、、、当我需要绘制很多图片时,当图片数量不固定时。。。我就需要他了

/**
 * @author 赵勇
 * @desc 下载图片数组
 *
 * @param images 图片url数组
 * @param success 成功回调
 * 
 */
function downloadImages(images, success, info = [], i = 0) {
    const wxGetImageInfo = utils.wxPromisify(wx.getImageInfo);
    wxGetImageInfo({
        src: images[I]
    }).then(res => {
        info = [...info, res];
        I++;
        if (i < images.length) {
            downloadImages(images, success, info, i);
        } else {
            success(info);
        }
    }).catch(err => {
        console.log(err)

    })
}

写了一个简单的demo,需要的就自取啦:GitHub微信小程序保存图片分享的 canvas 简易自用工具类

当然,欢迎star~~~~

上一篇下一篇

猜你喜欢

热点阅读