基于canvas商品海报生成源码分享

电商系统中,商品海报是必不可少的功能,下面以Javashop电商系统为例,分享基于canvas实现的海报生成思路。

这是一款基于canvas的商品海报生成组件,可以根据图片比例生成商品海报图,适用于商城海报分享功能,基于uniapp框架,兼容app、h5、小程序。

效果图

(app、h5、小程序):

使用方式:

在 script中引用组件(如果组件是在components内注册的不需要引入直接使用即可):

import goodsQrcodePoster from './-goods-qrcode-poster.vue'
components: {
     goodsQrcodePoster
},
methods: {
    //生成海报,调用组件内的方法(组件标签添加ref属性,父级调用子组件showCanvas()方法使用)
    handlePoster() {
        this.$refs.poster.showCanvas()
    }
}

在 template中使用组件:

<goods-qrcode-poster
    ref="poster"
    :goodsImg="商品图片地址"
    :goodsName="商品名称"
    :goodsId="商品ID"
    :price="商品价格"
    :promotion="商品促销活动"
    :qrcode="商品小程序码"
    :disParams="分享携带的参数"
></goods-qrcode-poster>

属性说明:

 

属性

 

类型

 

说明

 

goodsImg

 

String

 

商品图片链接

 

goodsName

 

String

 

商品名称

 

goodsId

 

String

 

商品ID

 

price

 

Number

 

商品价格

 

promotion

 

Object

 

商品促销活动

 

qrcode

 

String

 

商品小程序码链接

 

disParams

 

String

 

分享携带的参数

源码分享: 

<template>
    <view class="content" v-if="isShow" @click.stop="isShow = false">
        <canvas @click.stop="" disable-scroll="true" :style="{  canvasW + 'px', height: canvasH + 'px' }" canvas-id="my-canvas"></canvas>
        <view v-if="posterShow" class="save-btn" @click.stop="saveImage">保存图片</view>
    </view>
</template>
<script>
    import Qr from '@/common/wxqrcode.js'
    export default {
        props:{
            goodsImg:{
                type: String,
                default: ''
            },
            goodsName:{
                type: String,
                default: ''
            },
            goodsId: {
                type: String,
                default: ''
            },
            price:{
                type: Number,
                default: 0.00
            },
            promotion: {
                type: Object,
                default() {
                    return {}
                }
            },
            qrcode: {
                type: String,
                default: ''
            },
            disParams: {
                type: String,
                default: ''
            }
        },
        data(){
            return {
                canvasW: 0,
                canvasH: 0,
                ctx: null,
                isShow: false,
                posterShow: false,
                rpx: uni.getSystemInfoSync().windowWidth / 375
            }
        },
        methods: {
            //显示
            showCanvas(){
                this.isShow = true
                this.__init()
            },
            //初始化画布
            async __init() {
                uni.showLoading({
                    title: '图片生成中',
                    mask: true
                })
                this.ctx = uni.createCanvasContext('my-canvas',this)
                this.canvasW = uni.upx2px(520);
                this.canvasH = uni.upx2px(850);
                //设置画布背景透明
                this.ctx.setFillStyle('rgba(255, 255, 255, 0)')
                //设置画布大小
                this.ctx.fillRect(0,0,this.canvasW,this.canvasH)
                //绘制圆角背景
                this.drawRoundRect(this.ctx, 0, 0, this.canvasW, this.canvasH,uni.upx2px(20),'#FFFFFF')
                //获取商品图片
                let goodsImg = await this.getImageInfo(this.goodsImg)
                let hW = uni.upx2px(480);
                let hH = uni.upx2px(480);
                //绘制商品图
                this.drawRoundImg(this.ctx,goodsImg.path,((this.canvasW-hW) / 2),((this.canvasW-hW) / 2),hW,hH,8)
                let pointWidth = ''
                //活动价
                if (this.promotion && this.promotion.promotion_type) {
                    this.ctx.setFontSize(16);
                    this.ctx.setFillStyle('#FA3534');
                    this.ctx.fillText('¥',((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + 30))
                    this.ctx.setFontSize(18);
                    if (this.promotion.promotion_type === 'EXCHANGE') {
                        pointWidth = this.ctx.measureText(this.promotion.exchange.exchange_money).width + this.ctx.measureText(this.promotion.exchange.exchange_point).width + 150
                        this.ctx.fillText(this.promotion.exchange.exchange_money + ' + ' + this.promotion.exchange.exchange_point + '积分',(((this.canvasW-hW) / 2) + 14),(((this.canvasW-hW) / 2) + hH + 30))
                    } else if (this.promotion.promotion_type === 'SECKILL') {
                        pointWidth = this.ctx.measureText(this.promotion.seckill_goods_vo.seckill_price).width + 65
                        this.ctx.fillText(this.promotion.seckill_goods_vo.seckill_price,(((this.canvasW-hW) / 2) + 14),(((this.canvasW-hW) / 2) + hH + 30))
                    } else if (this.promotion.promotion_type === 'GROUPBUY') {
                        pointWidth = this.ctx.measureText(this.promotion.groupbuy_goods_vo.price).width + 65
                        this.ctx.fillText(this.promotion.groupbuy_goods_vo.price,(((this.canvasW-hW) / 2) + 14),(((this.canvasW-hW) / 2) + hH + 30))
                    }
                } else {
                    this.ctx.setFontSize(16);
                    this.ctx.setFillStyle('#FA3534');
                    this.ctx.fillText('¥',((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + 30))
                    this.ctx.setFontSize(20);
                    this.ctx.fillText(this.price,(((this.canvasW-hW) / 2) + 14),(((this.canvasW-hW) / 2) + hH + 30))
                }
                //活动
                if (this.promotion && this.promotion.promotion_type) {
                    this.ctx.setFontSize(12);
                    this.ctx.setFillStyle('#FA3534');
                    this.ctx.setStrokeStyle("#FA3534")//设置线条的颜色
                    this.ctx.setLineWidth(1)//设置线条宽度
                    if (this.promotion.promotion_type === 'EXCHANGE') {
                        this.ctx.fillText('积分活动' ,((this.canvasW-hW) / 2 + pointWidth + 6),(((this.canvasW-hW) / 2) + hH + 28))
                        this.ctx.strokeRect(((this.canvasW-hW) / 2 + pointWidth), (((this.canvasW-hW) / 2) + hH + 15), 60, 18);
                    } else if (this.promotion.promotion_type === 'SECKILL') {
                        this.ctx.fillText('限时抢购',((this.canvasW-hW) / 2 + pointWidth + 6),(((this.canvasW-hW) / 2) + hH + 28))
                        this.ctx.strokeRect(((this.canvasW-hW) / 2 + pointWidth), (((this.canvasW-hW) / 2) + hH + 15), 60, 18);
                    } else if (this.promotion.promotion_type === 'GROUPBUY') {
                        this.ctx.fillText('团购活动',((this.canvasW-hW) / 2 + pointWidth + 6),(((this.canvasW-hW) / 2) + hH + 28))
                        this.ctx.strokeRect(((this.canvasW-hW) / 2 + pointWidth), (((this.canvasW-hW) / 2) + hH + 15), 60, 18);
                    }
                }
                //原价
                if (this.promotion && this.promotion.promotion_type) {
                    this.ctx.setFontSize(12);
                    this.ctx.setFillStyle('#999999');
                    if (this.promotion.promotion_type === 'EXCHANGE') {
                        this.ctx.fillText('原价:¥' + this.promotion.exchange.goods_price,((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + 60))
                    } else if (this.promotion.promotion_type === 'SECKILL') {
                        this.ctx.fillText('原价:¥' + this.promotion.seckill_goods_vo.original_price,((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + 60))
                    } else if (this.promotion.promotion_type === 'GROUPBUY') {
                        this.ctx.fillText('原价:¥' + this.promotion.groupbuy_goods_vo.original_price,((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2) + hH + 60))
                    }
                }
                //绘制标题
                let row = this.newLine(this.goodsName, this.ctx)
                let a = 20//定义行高20
                for (let i = 0; i < row.length; i++) {
                    this.ctx.setFontSize(14)
                    this.ctx.setFillStyle("#000000")
                    this.ctx.fillText(row[i], ((this.canvasW-hW) / 2),(((this.canvasW-hW) / 2 + a * i) + hH + 100))
                }
                //小程序码 二维码
                // #ifdef APP-PLUS || H5
                let qrcodeImg = Qr.createQrCodeImg( `分享链接,扫码跳转的页面`)
                this.ctx.drawImage(qrcodeImg, 165 * this.rpx,(((this.canvasW-hW) / 2) + hH + 80), 75 * this.rpx, 75 * this.rpx)
                // #endif
                // #ifdef MP
                let qrcodeImg = await this.getImageInfo(this.qrcode)
                this.ctx.drawImage(qrcodeImg.path, 170 * this.rpx,(((this.canvasW-hW) / 2) + hH + 80), 75 * this.rpx, 75 * this.rpx)
                // #endif
                //延迟渲染
                setTimeout(()=>{
                    this.ctx.draw(true,()=>{
                        uni.hideLoading()
                        this.posterShow = true
                    })
                },500)
            },
            //带圆角图片
            drawRoundImg(ctx, img, x, y, width, height, radius){
                ctx.beginPath()
                ctx.save()
                // 左上角
                ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
                // 右上角
                ctx.arc(x + width - radius, y + radius, radius, Math.PI * 1.5, Math.PI * 2)
                // 右下角
                ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI * 0.5)
                // 左下角
                ctx.arc(x + radius, y + height - radius, radius, Math.PI * 0.5, Math.PI)
                ctx.stroke()
                ctx.clip()
                ctx.drawImage(img, x, y, width, height);
                ctx.restore()
                ctx.closePath()
            },
            //圆角矩形
            drawRoundRect(ctx, x, y, width, height, radius, color){
                ctx.save();
                ctx.beginPath();
                ctx.setFillStyle(color);
                ctx.setStrokeStyle(color)
                ctx.setLineJoin('round');  //交点设置成圆角
                ctx.setLineWidth(radius);
                ctx.strokeRect(x + radius/2, y + radius/2, width - radius , height - radius );
                ctx.fillRect(x + radius, y + radius, width - radius * 2, height - radius * 2);
                ctx.stroke();
                ctx.closePath();
            },
            // canvas多文字换行
            newLine(txt, context) {
                let txtArr = txt.split('')
                let temp = ''
                let row = []
                for (let i = 0; i < txtArr.length; i++) {
                    // #ifdef H5 || MP
                    if (context.measureText(temp).width < 130 * this.rpx) {
                        temp += txtArr[i]
                    }
                    // #endif
                    // #ifdef APP-PLUS
                    if (temp.length < 12) {
                        temp += txtArr[i]
                    }
                    // #endif
                    else {
                        i--
                        row.push(temp)
                        temp = ''
                    }
                }
                row.push(temp)
                //如果数组长度大于3 则截取前三个
                if (row.length > 3) {
                    let rowCut = row.slice(0, 3);
                    let rowPart = rowCut[2];
                    let test = "";
                    let empty = [];
                    for (let a = 0; a < rowPart.length; a++) {
                      if (context.measureText(test).width < 130 * this.rpx) {
                        test += rowPart[a];
                      } else {
                        break;
                      }
                    }
                    empty.push(test);
                    let group = empty[0] + "..." //这里只显示三行,超出的用...表示
                    rowCut.splice(2, 1, group);
                    row = rowCut;
                }
                return row
            },
            //获取图片
            getImageInfo(imgSrc){
                return new Promise((resolve, reject) => {
                    uni.getImageInfo({
                        src: imgSrc,
                        success: (image) => {
                            resolve(image);
                        },
                        fail: (err) => {
                            reject(err);
                        }
                    });
                });
            },
            //保存图片到相册
            saveImage(e){
                // #ifdef MP
                //判断用户授权
                uni.getSetting({
                   success(res) {
                      if(Object.keys(res.authSetting).length>0){
                          //判断是否有相册权限
                          if(res.authSetting['scope.writePhotosAlbum']==undefined){
                              //打开设置权限
                              uni.openSetting({
                                success(res) {
                                  console.log('设置权限',res.authSetting)
                                }
                              })
                          }else{
                              if(!res.authSetting['scope.writePhotosAlbum']){
                                  //打开设置权限
                                  uni.openSetting({
                                    success(res) {
                                      console.log('设置权限',res.authSetting)
                                    }
                                  })
                              }
                          }
                      }else{
                          return
                      }
                   }
                })
                // #endif
                let that = this
                uni.canvasToTempFilePath({
                    canvasId: 'my-canvas',
                    quality: 1,
                    complete: (res) => {
                        uni.saveImageToPhotosAlbum({
                            filePath: res.tempFilePath,
                            success(res) {
                                that.isShow = false
                                uni.showToast({
                                    title: '已保存到相册',
                                    icon: 'success',
                                    duration: 2000
                                })
                            }
                        })
                    }
                },that);
            }
        }
    }
</script>
<style scoped lang="scss">
.content{
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 999;
    background: rgba(0,0,0,.4);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    .save-btn{
        margin-top: 35rpx;
        color: #FFFFFF;
        background: linear-gradient(to right, #FD5632 0%, #EF0D25 100%);
        padding: 20rpx 200rpx;
        border-radius: 50rpx;
    }
}
</style>
原文地址:https://www.cnblogs.com/javashop-docs/p/14103081.html