zoukankan      html  css  js  c++  java
  • 基于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>
  • 相关阅读:
    Java的注释,标识符,标识符的命名规范
    Java入门程序(Java的开发流程)
    EasyMock使用总结
    【转载】 Java并发编程:深入剖析ThreadLocal
    java.util.Stack类中的peek()方法
    Jquery和Ajax
    关于HTTP协议及SOCKET通信
    (转载)session token机制
    TCP/IP知识总结(TCP/IP协议族读书笔记四)
    TCP/IP知识总结(TCP/IP协议族读书笔记三)
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/14103081.html
Copyright © 2011-2022 走看看