uni-app 点击生成海报

本文介绍了一种使用uni-app框架在前端生成商品分享海报的方法,并详细解释了如何利用canvas实现兼容H5和小程序的海报生成流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前端生成海报兼容H5和小程序

最近做项目需求是生成商品分享海报,并且保存到手机中要兼容H5和小程序

与后端同学沟通后,海报在前端生成最省性能和有较好的交互体验,先看做好的效果

源码地址
在这里插入图片描述

在这里插入图片描述

前端框架使用的是uni-app方便打包成H5和小程序

实现方案是拿到后端返回的数据后,利用canvas画布把各个数据拼在一起并且生成一张图片

主要的参数有:背景图、商品图、二维码、价格、原价、标题

首先拿到产品图和二维码之后需要把它们下载下来用uni-app的api就可以

先写一个下载方法并且在 template 定义画布组件

<template>
 
<canvas class="canvas" canvas-id="myCanvas" v-if="canvasStatus"></canvas>
 
</template>
 
onReady(){
 
  this.downloadFileImg('','pic');
 
  this.downloadFileImg('','code');
 
},
 
methods:{
 
  downloadFileImg(url,name){
 
    let self = this
 
    uni.downloadFile({
 
      url: url,
 
      success: function(res) {
 
        self[name] = res.tempFilePath;
 
      },
 
      fail: function(erros) {
 
        console.log(error)
 
      }
 
    });
 
  }
 
}

这样图片就暂时保存到本地临时文件了

uni.downloadFile 需要注意的是

在各个小程序平台运行时,网络相关的 API 在使用前需要配置域名白名单。在h5上是跨域的,用户需要处理好跨域问题。

下来编写canvas生成图片的方法


/**
    * 获取分享海报
    * @param array imgArr 海报素材 0 背景图 1商品图 2二维码
    * @param string store_name 素材文字
    * @param string price 价格
    * @param string ot_price 原始价格
    * @param function successFn 回调函数
*/
 
PosterCanvas: function(imgArr, store_name, price, ot_price, successFn) {
 
    let that = this;
 
    uni.showLoading({
 
        title: '海报生成中',
 
        mask: true
 
    });
 
    const ctx = uni.createCanvasContext('myCanvas');
 
    ctx.clearRect(0, 0, 0, 0);
 
 
 
    /**
    * 只能获取合法域名下的图片信息,本地调试无法获取
    * 
    */
 
    ctx.fillStyle = '#fff';
 
    ctx.fillRect(0, 0, 750, 1150);
 
    uni.getImageInfo({
 
        src: imgArr[0],
 
        success: function(res) {
 
            const WIDTH = res.width;
 
            const HEIGHT = res.height;
 
            ctx.drawImage(imgArr[1], 0, 0, WIDTH, WIDTH);
 
            ctx.save();
 
            let r = 110;
 
            let d = r * 2;
 
            let cx = 480;
 
            let cy = 790;
 
            ctx.arc(cx + r, cy + r, r, 0, 2 * Math.PI);
 
            // ctx.clip();
 
            ctx.drawImage(imgArr[2], cx, cy, d, d);
 
            ctx.restore();
 
            const CONTENT_ROW_LENGTH = 20;
 
            let [contentLeng, contentArray, contentRows] = that.textByteLength(store_name, CONTENT_ROW_LENGTH);
 
            if (contentRows > 2) {
 
                contentRows = 2;
 
                let textArray = contentArray.slice(0, 2);
 
                textArray[textArray.length - 1] += '……';
 
                contentArray = textArray;
 
            }
 
            ctx.setTextAlign('left');
 
            ctx.setFontSize(36);
 
            ctx.setFillStyle('#000');
 
            // let contentHh = 36 * 1.5;
 
            let contentHh = 36;
 
            for (let m = 0; m < contentArray.length; m++) {
 
                if (m) {
 
                    ctx.fillText(contentArray[m], 50, 1000 + contentHh * m + 18, 1100);
 
                } else {
 
                    ctx.fillText(contentArray[m], 50, 1000 + contentHh * m, 1100);
 
                }
 
            }
 
            ctx.setTextAlign('left')
 
            ctx.setFontSize(72);
 
            ctx.setFillStyle('#DA4F2A');
 
            ctx.fillText('¥' + price, 40, 820 + contentHh);
 
 
 
            ctx.setTextAlign('left')
 
            ctx.setFontSize(36);
 
            ctx.setFillStyle('#999');
 
            ctx.fillText('¥' + ot_price, 50, 876 + contentHh);
 
            var underline = function(ctx, text, x, y, size, color, thickness, offset) {
 
                var width = ctx.measureText(text).width;
 
                switch (ctx.textAlign) {
 
                    case "center":
 
                        x -= (width / 2);
 
                        break;
 
                    case "right":
 
                        x -= width;
 
                        break;
 
                }
 
 
 
                y += size + offset;
 
                ctx.beginPath();
 
                ctx.strokeStyle = color;
 
                ctx.lineWidth = thickness;
 
                ctx.moveTo(x, y);
 
                ctx.lineTo(x + width, y);
 
                ctx.stroke();
 
            }
 
            underline(ctx, '¥' + ot_price, 55, 865, 36, '#999', 2, 0)
 
            ctx.setTextAlign('left')
 
            ctx.setFontSize(28);
 
            ctx.setFillStyle('#999');
 
            ctx.fillText('长按或扫描查看', 490, 1030 + contentHh);
 
            ctx.draw(true, function() {
 
                uni.canvasToTempFilePath({
 
                    canvasId: 'myCanvas',
 
                    fileType: 'png',
 
                    destWidth: WIDTH,
 
                    destHeight: HEIGHT,
 
                    success: function(res) {
 
                        uni.hideLoading();
 
                        successFn && successFn(res.tempFilePath);
 
                    }
 
                })
 
            });
 
        },
 
        fail: function(err) {
 
            uni.hideLoading();
 
            that.Tips({
 
                title: '无法获取图片信息'
 
            });
 
        }
 
    })
 
}

首先创建一个canvas 画布

获取背景图图片信息拿到宽和高再绘制商品图片并保存

接下来绘制二维码并把坐标放好并保存

在处理文字换行问题并设置大小颜色和对其方式

在相对应的设置价格和原价的颜色和大小还有坐标

由于价格还有条横线,我在网上又搜下了横线的做法直接看方法就行

最后生成图片信息并且使用uni.canvasToTempFilePath 方法把当前画布指定区域的内容导出生成指定大小的图片,并返回文件路径。

这样我们就得到一个.png的临时文件现在就剩最后一步把文件渲染到组件里了,从回调函数就可以回去到

此方法用的比方比较多我把它写到公共方法里面并且绑定到vue原型上面方便我们后面使用

现在编写方法调用

handelCanvas(){
 
    let imgArr = ['背景图路径',this.pic,this.code]
 
    this.$util.PosterCanvas(imgArr,'标题','价格','原价',function(tempFilePath){
 
        console.log(tempFilePath)
 
    })
 
}

这样就拿到生成好的图片的是一个临时文件 现在把他放到页面中展示就ok了

保存图片功能H5可以长按保存图片这里只用处理小程序的就行

首先检测授权拿到授权后调用uni-app的api就可以了

savePosterPath: function() {
 
    let that = this;
 
    uni.getSetting({
 
        success(res) {
 
            if (!res.authSetting['scope.writePhotosAlbum']) {
 
                uni.authorize({
 
                    scope: 'scope.writePhotosAlbum',
 
                    success() {
 
                        uni.saveImageToPhotosAlbum({
 
                            filePath: 'canvas生成的临时图片',
 
                            success: function(res) {
 
                                ....成功了
 
                            },
 
                            fail: function(res) {
 
                                ....失败了
 
                            }
 
                        });
 
                    }
 
                });
 
            } else {
 
                uni.saveImageToPhotosAlbum({
 
                    filePath: 'canvas生成的临时图片',
 
                    success: function(res) {
 
                        ....成功了
 
                    },
 
                    fail: function(res) {
 
                        ....失败了
 
                    }
 
                });
 
            }
 
        }
 
    });
 
}

这样前端生成海报就大功告成了,你学废了吗?

开源代码

<think>嗯,用户想了解如何使用uni-app开发一个模拟影院购票系统。首先,我需要整理开发这样的系统需要哪些步骤和关键点。根据提供的引用资料,特别是引用[3]和[5],它们提到了基于Spring Boot和Vue.jsuni-app的电影订票系统,可能涉及前后端的技术栈。不过用户主要关注uni-app部分,所以应该重点放在前端实现上。 首先,系统的基本功能应该包括影院列表、电影详情、场次选择、座位选择和订单确认。这些功能模块需要逐一分析。比如,影院列表可以用uni-app的滚动视图展示,电影详情页需要展示海报、简介等信息。场次选择可能需要调用后端API获取数据,但在这个示例中,可能先使用静态数据模拟。 接下来是技术选型。uni-app本身支持多端发布,所以需要使用Vue.js的语法结构。页面结构部分,每个功能模块对应一个页面或组件。比如,影院列表可以用<scroll-view>组件,电影详情用<image>和<text>组件展示。座位选择部分比较复杂,可能需要使用网格布局或canvas来绘制座位图,并处理用户的点击事件。 然后是数据管理。虽然引用中提到了Spring Boot作为后端,但用户可能暂时只需要前端模拟。因此,可以使用本地数据或Mock数据来模拟影院、电影、场次等信息。例如,在data()中定义movies和schedules数组,存储静态数据。 座位选择功能的实现是关键。考虑到用户交互,每个座位应该是一个点击的元素,点击后切换选中状态。可以使用二维数组表示座位的状态,0代表可选,1代表已售,2代表选中。当用户点击座位时,更新对应的状态并计算选中的座位数和总价。 订单确认页面需要收集用户信息,如表单输入,并验证手机号格式。这里需要用到正则表达式进行验证,并在提交时检查必填字段是否填写。 另外,引用[2]提到使用uni-calendar组件展示日期相关的数据,可能在场次选择时可以用到日历组件让用户选择日期。不过示例中可能简化成直接选择场次时间。 在代码示例部分,需要确保符合uni-app的语法,比如使用vue的单文件组件结构,页面跳转使用uni.navigateTo,以及样式使用flex布局适配不同屏幕。 测试和调试部分,可以使用HBuilderX进行真机调试,确保各功能在移动端正常运行。部署方面,生成小程序代码包并上传到微信开发者工具。 可能遇到的问题包括座位布局的响应式设计、状态管理和跨页面数据传递。可以使用Vuex进行状态管理,或者在页面跳转时通过URL参数传递数据。 最后,根据用户要求生成相关问题,需要考虑开发中的常见难点,比如数据同步、性能优化、第三方支付集成等,以及如何扩展到多端发布。</think>以下是一个基于uni-app开发影院购票系统的分步骤实现方案,结合引用资料中的技术要点: ### 一、系统核心功能设计 1. **影院/电影列表展示**:使用`<scroll-view>`实现横向滚动影院列表,纵向滚动电影列表[^2][^5] 2. **场次选择功能**:通过`<picker>`组件选择日期和时间,展示可选场次信息[^2] 3. **座位可视化选择**:使用网格布局实现影院座位图(0=可选,1=已售,2=选中)[^5] 4. **订单生成与支付**:集成微信支付API(需配置商户号)[^3] ### 二、技术实现示例 ```vue <!-- pages/cinema/seats.vue --> <template> <view class="seat-container"> <view v-for="(row, i) in seats" :key="i" class="seat-row"> <view v-for="(seat, j) in row" :key="j" :class="['seat', seatClasses[seat]]" @click="toggleSeat(i, j)" ></view> </view> </view> </template> <script> export default { data() { return { seats: [ [0,0,1,0], [0,1,1,0], [0,0,0,0] ], seatClasses: ['available', 'sold', 'selected'] } }, methods: { toggleSeat(row, col) { if(this.seats[row][col] === 0) { this.$set(this.seats[row], col, 2) } else if(this.seats[row][col] === 2) { this.$set(this.seats[row], col, 0) } } } } </script> <style> .seat-container { padding: 20rpx; } .seat-row { display: flex; justify-content: center; margin: 10rpx 0; } .seat { width: 60rpx; height: 60rpx; margin: 5rpx; border-radius: 8rpx; } .available { background-color: #4CD964; } .sold { background-color: #DD524D; } .selected { background-color: #007AFF; } </style> ``` ### 三、关键实现步骤 1. **数据初始化**:通过`onLoad`生命周期加载影院数据 ```js async loadCinemaData() { const res = await uni.request({ url: 'https://api.example.com/cinemas', method: 'GET' }) this.cinemas = res.data } ``` 2. **订单生成逻辑** ```js generateOrder() { const selectedSeats = this.seats .flatMap((row, i) => row.map((seat, j) => seat === 2 ? `${i+1}排${j+1}座` : null) ) .filter(Boolean) if(selectedSeats.length === 0) { uni.showToast({ title: '请选择座位', icon: 'none' }) return } uni.navigateTo({ url: `/pages/order/confirm?seats=${encodeURIComponent(selectedSeats.join(','))}` }) } ``` 3. **支付集成**(需配置微信支付) ```js handlePayment() { uni.requestPayment({ provider: 'wxpay', orderInfo: JSON.stringify({ // 从服务端获取的支付参数 timeStamp: '', nonceStr: '', package: '', signType: 'MD5', paySign: '' }), success: () => { uni.showToast({ title: '支付成功' }) } }) } ``` ### 四、部署调试建议 1. 使用HBuilderX进行真机调试 2. 生成微信小程序包时需配置`appid` 3. 支付功能需要企业资质(开发阶段可用模拟支付)[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值