赞
踩
uniapp_vue3版本做微信小程序项目,生成海报功能,使用canvas进行实现,适配各种型号手机。
这张海报宽690px,长1180px,是由背景图片以及文字组成的,均可替换。
创建一个poster.vue文件,内容如下:
此代码script使用setup,样式使用scss。使用时把 state ->myObj->bgImg值换成 '你背景图的地址'即可。
- <template>
- <!--页面属性配置节点,设置页面跟字体大小 -->
- <page-meta :root-font-size="fontSize + 'px'"></page-meta>
- <view class="posPoster">
- <view class="percard">
- <canvas v-show="isshow" canvas-id="myCanvas" :style="{ width: canvasWidth, height: canvasHeight }"></canvas>
- </view>
- <!-- 底部按钮占位,防止底部按钮挡住海报 -->
- <view class="bottomPosHeight"></view>
- </view>
- <view class="sureEvaluate" @tap="shareFriends">海报操作动作面板</view>
- </template>
-
- <script setup>
- import { reactive, toRefs, computed, onMounted, ref, watchEffect } from 'vue';
- import { onLoad } from '@dcloudio/uni-app';
-
- const state = reactive({
- fontSize: '',
- isshow: true,
- isShowBtn: false,
- myObj: {
- introduction: '海报二级标题',
- bgImg: '你背景图的地址', //背景图
- name: '开心小老虎',
- subject: '幼儿园',
- grade: '大班',
- time: '5月9日 8:00-9:00',
- position: '北京 XXX XXX XXX XXX',
- nameA: '姓名',
- subjectA: '年级',
- gradeA: '班级',
- timeA: '上课时间',
- positionA: '上课地点',
- share: `我的世界我做主,开心就好!`
- },
- canvasWidth: 690, //画布宽度
- canvasHeight: 1180, //画布高度
- ratio: 0, //计算UI设计稿和你手机的屏幕宽度比例(例如UI设计稿是750宽度 你手机是350宽度 比例就是2 那么你画布画图时候 所有的尺寸大小、宽高、位置、定位左右上下都需要除以 / 比例2 )
- widths: '',
- heights: '',
- imgs: ''
- });
- const { isShowBtn, fontSize, isshow, myObj, canvasWidth, canvasHeight, ratio, widths, heights, imgs } = toRefs(state);
-
- // #ifdef MP-WEIXIN
- /*
- *微信小程序获得手机信息(布局视口宽度),微信官方已不在维护
- */
- wx.getSystemInfo({
- success: function (res) {
- const rootFontSize = (res.windowWidth * 100) / 750;
- state.fontSize = rootFontSize;
- }
- });
-
- /**
- * 海报操作面板
- */
- const shareFriends = () => {
- wx.showShareImageMenu({
- path: state.imgs,
- success: (res) => {
- console.log('操作成功:', res);
- },
- fail: (err) => {
- console.log('操作取消:', err);
- }
- });
- };
- // #endif
-
- /**
- * 计算海报尺寸,适配各种手机,因为我的项目没有使用rpx用的rem所以需要换算,后来为了提出单独组件,在scss里用了rpx。
- */
- onLoad((option) => {
- uni.getSystemInfo({
- success: (res) => {
- state.canvasWidth = (res.screenWidth / 750) * 690 + 'px';
- state.widths = (res.screenWidth / 750) * 690;
- state.ratio = 750 / res.screenWidth;
- state.canvasHeight = (res.screenWidth / 750) * 1180 + 'px';
- state.heights = (res.screenWidth / 750) * 1180;
- }
- });
- uni.showLoading({
- title: '海报生成中...'
- });
- downImgUrl();
- });
-
- const downImgUrl = () => {
- uni.getImageInfo({
- src: state.myObj.bgImg,
- success: function (res) {
- state.myObj.bgImg = res.path;
- drawPageImg();
- state.isShowBtn = true;
- }
- });
- };
-
- //画一个带圆角矩形
- const ctxCircular = (ctx, img, x, y, width, height, r, shadow) => {
- ctx.beginPath(); //开始绘制
- ctx.save(); //保存(canvas)状态
- ctx.moveTo(x + r, y);
- ctx.lineTo(x + width - r, y);
- ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
- ctx.lineTo(x + width, y + height - r);
- ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5);
- ctx.lineTo(x + r, y + height);
- ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI);
- ctx.lineTo(x, y + r);
- ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
- if (shadow == 1) {
- ctx.shadowBlur = 20; // 模糊效果程度的
- ctx.shadowColor = 'red'; // 阴影颜色
- }
- ctx.fill(); //对当前路径中的内容进行填充
- ctx.clip(); //从原始画布中剪切任意形状和尺寸
- ctx.closePath(); //关闭一个路径
- ctx.drawImage(img, x, y, width, height);
- ctx.restore(); //恢复(canvas)状态
- ctx.globalCompositeOperation = 'source-over';
- };
-
- //画一个矩形也就是整个海报的背景
- const ctxRectangle = (ctx, x, y, width, height, r, gnt) => {
- ctx.beginPath();
- ctx.save(); //保存状态
- ctx.moveTo(x + r, y);
- ctx.lineTo(x + width - r, y);
- ctx.arc(x + width - r, y + r, r, Math.PI * 1.5, Math.PI * 2);
- ctx.lineTo(x + width, y + height - r);
- ctx.arc(x + width - r, y + height - r, r, 0, Math.PI * 0.5);
- ctx.lineTo(x + r, y + height);
- ctx.arc(x + r, y + height - r, r, Math.PI * 0.5, Math.PI);
- ctx.lineTo(x, y + r);
- ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);
- ctx.fillStyle = gnt;
- ctx.fill(); //对当前路径中的内容进行填充
- ctx.closePath(); //关闭一个路径
- };
-
- // 文字
- const ctxText = (ctx, textFont, textAlign, textFillStyle, textName, x, y) => {
- ctx.beginPath();
- ctx.save(); //保存状态
- //字体
- (ctx.font = textFont),
- //字体样式
- (ctx.textAlign = textAlign);
- //字体颜色
- ctx.fillStyle = textFillStyle;
- //填充字体
- ctx.fillText(textName || '', x, y);
- ctx.globalCompositeOperation = 'source-over';
- };
-
- // 文字
- const ctxTextWrap = (ctx, text, x, y, w, size) => {
- //自动换行介绍
- var temp = '';
- var row = [];
- let gxqm = '';
- if (text) {
- gxqm = text;
- } else {
- gxqm = '未设置个性签名';
- }
- let gexingqianming = gxqm.split('');
- for (var a = 0; a < gexingqianming.length; a++) {
- if (ctx.measureText(temp).width < w) {
- } else {
- row.push(temp);
- temp = '';
- }
- temp += gexingqianming[a];
- }
- row.push(temp);
- ctx.font = `${size}px arail`;
- ctx.textAlign = 'left';
- ctx.fillStyle = '#333';
- ctx.globalCompositeOperation = 'source-over';
- for (var b = 0; b < row.length; b++) {
- ctx.fillText(row[b] || '', x, y + (b + 1) * 20);
- }
- };
-
- // 文字
- const ctxTextWrapA = (ctx, text, x, y, w, size, color) => {
- //自动换行介绍
- var temp = '';
- var row = [];
- let gxqm = '';
- if (text) {
- gxqm = text;
- } else {
- gxqm = '';
- }
- let gexingqianming = gxqm.split('');
- for (var a = 0; a < gexingqianming.length; a++) {
- if (ctx.measureText(temp).width < w) {
- } else {
- row.push(temp);
- temp = '';
- }
- temp += gexingqianming[a];
- }
- row.push(temp);
- ctx.font = `${size}px arail`;
- ctx.textAlign = 'left';
- ctx.fillStyle = `#${color}`;
- ctx.globalCompositeOperation = 'source-over';
- for (var b = 0; b < row.length; b++) {
- ctx.fillText(row[b] || '', x, y + (b + 1) * 20);
- }
- };
-
- // 文字
- const ctxTextWrapB = (ctx, text, x, y, w, size, color) => {
- //自动换行介绍
- var temp = '';
- var row = [];
- let gxqm = '';
- if (text) {
- gxqm = text;
- } else {
- gxqm = '未设置个性签名';
- }
- let gexingqianming = gxqm.split('');
- for (var a = 0; a < gexingqianming.length; a++) {
- if (ctx.measureText(temp).width < w) {
- } else {
- row.push(temp);
- temp = '';
- }
- temp += gexingqianming[a];
- }
- row.push(temp);
- ctx.font = `arail normal bold ${size}px sans-serif`;
- ctx.textAlign = 'left';
- ctx.fillStyle = `#${color}`;
- ctx.globalCompositeOperation = 'source-over';
- for (var b = 0; b < row.length; b++) {
- ctx.fillText(row[b] || '', x, y + (b + 1) * 20);
- }
- };
-
- // 使用画布绘制页面
- const drawPageImg = () => {
- // 生成画布
- const ctx = reactive(uni.createCanvasContext('myCanvas'));
- // 绘制背景
- ctx.drawImage(
- state.myObj.bgImg, //图像资源
- 0 / state.ratio, //图像的左上角在目标canvas上 X 轴的位置
- 0 / state.ratio, //图像的左上角在目标canvas上 Y 轴的位置
- 690 / state.ratio, //在目标画布上绘制图像的宽度
- 1180 / state.ratio //在目标画布上绘制图像的高度
- );
- let numA = Math.floor(0.28 * state.fontSize);
- ctxTextWrapB(ctx, state.myObj.introduction, 62 / state.ratio, 374 / state.ratio, 600 / state.ratio, Math.floor(0.38 * state.fontSize), '333');
- ctxTextWrapA(ctx, state.myObj.nameA, 62 / state.ratio, 473 / state.ratio, 400 / state.ratio, numA, '666');
- ctxTextWrapA(ctx, state.myObj.subjectA, 62 / state.ratio, 551 / state.ratio, 400 / state.ratio, numA, '666');
- ctxTextWrapA(ctx, state.myObj.gradeA, 62 / state.ratio, 629 / state.ratio, 400 / state.ratio, numA, '666');
- ctxTextWrapA(ctx, state.myObj.timeA, 62 / state.ratio, 707 / state.ratio, 400 / state.ratio, numA, '666');
- ctxTextWrapA(ctx, state.myObj.positionA, 62 / state.ratio, 785 / state.ratio, 400 / state.ratio, numA, '666');
-
- let num = Math.floor(0.32 * state.fontSize);
- ctxTextWrapA(ctx, state.myObj.name, 215 / state.ratio, 474 / state.ratio, 400 / state.ratio, num, '333');
- ctxTextWrapA(ctx, state.myObj.subject, 215 / state.ratio, 552 / state.ratio, 400 / state.ratio, num, '333');
- ctxTextWrapA(ctx, state.myObj.grade, 215 / state.ratio, 630 / state.ratio, 400 / state.ratio, num, '333');
- ctxTextWrapA(ctx, state.myObj.time, 215 / state.ratio, 708 / state.ratio, 400 / state.ratio, num, '333');
- ctxTextWrapA(ctx, state.myObj.position, 215 / state.ratio, 786 / state.ratio, 400 / state.ratio, num, '333');
- ctxTextWrapA(ctx, state.myObj.share, 62 / state.ratio, 275 / state.ratio, 550 / state.ratio, Math.floor(0.36 * state.fontSize), '333');
-
- ctx.draw(
- false,
- (() => {
- setTimeout(() => {
- // 将canvas 变成图片方便发送给好友或者保存
- uni.canvasToTempFilePath({
- canvasId: 'myCanvas',
- destWidth: state.canvasWidth * 2, //展示图片尺寸=画布尺寸1*像素比2
- destHeight: state.canvasHeight * 2,
- quality: 1,
- fileType: 'jpg',
- success: (res) => {
- uni.hideLoading();
- state.imgs = res.tempFilePath;
- },
- fail: function (error) {
- uni.hideLoading();
- uni.showToast({
- icon: 'none',
- position: 'bottom',
- title: '绘制图片失败'
- });
- }
- });
- }, 100);
- })()
- );
- };
- </script>
-
- <style scoped lang="scss">
- .posPoster {
- width: 750rpx;
- height: 1338rpx;
- position: relative;
- margin-top: 205rpx;
- }
- .percard {
- width: 690rpx;
- height: 1180rpx;
- overflow: hidden;
- position: absolute;
- left: 50%;
- transform: translate(-50%, 0);
- }
- .sureEvaluate {
- width: 690rpx;
- height: 88rpx;
- line-height: 88rpx;
- border-radius: 53rpx;
- background: #e6e8ff;
- @include fcsw(#5662f6, 32, center);
- position: fixed;
- bottom: 50rpx;
- left: 30rpx;
- border: none;
- outline: none;
- box-shadow: none;
- z-index: 999;
- }
- .bottomPosHeight {
- width: 750rpx;
- height: 138rpx;
- }
- </style>
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。