• 基于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>
  • 相关阅读:
    zbb20181207 springboot @ConfigurationProperties使用
    zbb20181206 logback,lombok 默认日志logback配置解析
    Spring Boot (8) 全局异常处理
    Spring Boot (7) JdbcTemplate访问数据库
    Spring Boot (6) Spring Data JPA
    Spring Boot (4) 静态页面和Thymeleaf模板
    Spring Boot (3) 热部署devtools
    Spring Boot (2) Restful风格接口
    Spring Boot (1) 构建第一个Spring Boot工程
    idea使用maven搭建ssm框架实现登陆商品增删改查
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/14103081.html
Copyright © 2020-2023  润新知