主页面
<template>
<view class="page">
<!-- 自定义导航栏-->
<Navbar title="我的海报"></Navbar>
<view class="container">
<poster ref="poster" :imageUrl="image" :imageWidth="750" :imageHeight="1000" :drawData="drawData"
:config="config" :wechatCode="false" @wechatCodeConfig="wechatCodeConfig" @click="createdPoster"
@loading="onLoading" @success="onSuccess" @fail="onFail" class="poster">
<!-- <template v-slot:save>
<view @click="saveToAlbum">保存</view>
</template> -->
</poster>
<u-image width="222rpx" height="222rpx" :src="code" class="container_image"></u-image>
</view>
<!-- <view class="bottom"> -->
<!-- <view class="bottom_item" @click="share('微信好友')">
<u-image width="74rpx" height="74rpx"
src="xxxxxxxxxxxxxxstatic/poster/%E7%BC%96%E7%BB%84%206%402x.png"></u-image>
<text>微信好友</text>
</view> -->
<!-- <view class="bottom_item" @click="share('朋友圈')">
<u-image width="74rpx" height="74rpx"
src="xxxxxxxxxxxposter/%E7%BC%96%E7%BB%84%208%402x.png"></u-image>
<text>朋友圈</text>
</view> -->
<!-- <view class="bottom_item" @click="share('保存图片')">
<u-image width="74rpx" height="74rpx"
src="xxxxxxxxxoster/%E7%BC%96%E7%BB%84%2012%402x.png"></u-image>
<text>保存图片</text>
</view> -->
<!-- </view> -->
</view>
</template>
<script>
import poster from "@/components/poster/index";
import {
saveImageToPhotosAlbum
} from '@/utils/poster.js';
export default {
components: {
poster
},
data() {
return {
code: '',
canvasImages: '',
image: 'xxxxxxter/static/poster/%E7%BC%96%E7%BB%84%205%402x.png',
config: {
imageMode: 'aspectFit',
posterHeight: '100%',
// canvasWidth 和 convasHeight使用的是px,防止不同设备Dpr不统一,导致最后图片留白边问题
canvasWidth: 275,
convasHeight: 600,
},
drawData: [],
wechatCodeConfig: {
serverUrl: '',
scene: '123123',
config: {
x: 84.5,
y: 320,
w: 100,
h: 100
}
}
}
},
created() {
this.usercode()
// 模拟异步请求获得到的数据
// setTimeout(() => {
// this.wechatCodeConfig.scene = '456787';
// this.drawData = [{
// type: 'image',
// config: {
// url: 'https://horifon.oss-cn-shanghai.aliyuncs.com/hongyunartcenter/static/poster/%E7%BC%96%E7%BB%84%205.png',
// x: 0,
// y: 0,
// w: 275,
// h: 490
// },
// },
// {
// type: 'image',
// config: {
// url: this.code,
// x: 40,
// y: 380,
// w: 40,
// h: 40
// },
// },
// {
// type: 'text',
// config: {
// text: '',
// x: 140,
// y: 60,
// color: '#E60012',
// font: 'normal bold 16px 仿宋',
// textAlign: 'center'
// }
// }
// ];
// }, 1000)
// this.createdPoster();
},
methods: {
//二维码
usercode() {
let $this = this
uni.request({
url: "xxxxxxx/mobile/index.php?m=user&c=indexapi&a=affiliate",
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"user_id": parseInt(uni.getStorageSync('USER_ID'))
},
success(res) {
console.log(res.data.data, '数据');
$this.code = res.data.data
$this.wechatCodeConfig.serverUrl = res.data.data
}
});
},
// 保存到相册
saveToAlbum() {
this.$refs.poster.saveToAlbum()
},
onLoading() {
console.log('Loading:正在加载静态资源')
},
onSuccess(e) {
console.log('Success:创建海报成功', e)
this.canvasImages = e
wx.showShareImageMenu({
path: this.canvasImages,
style:"background-color:'black'"
})
},
onFail(e) {
console.log('Fail:创建海报失败', e)
},
createdPoster() {
console.log('点击')
// 调用 createImage 开始创建海报
// this.$refs.poster.createImage()
this.$refs.poster.cjian()
// wx.showShareImageMenu({
// path: this.canvasImages
// })
},
share(e) {
if (e === '微信好友') {
wx.showShareImageMenu({
path: this.canvasImages
})
} else if (e === '朋友圈') {
uni.share({
provider: "weixin",
scene: "WXSceneTimeline",
type: 2,
imageUrl: this.canvasImages,
success: function(res) {
console.log("success:" + JSON.stringify(res));
},
fail: function(err) {
console.log("fail:" + JSON.stringify(err));
}
});
} else if (e === '保存图片') {
saveImageToPhotosAlbum(this.canvasImages).then(res => {
uni.showToast({
icon: 'none',
title: '保存成功'
})
}).catch(err => {
})
}
}
}
}
</script>
<style scoped lang="scss">
.container {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.poster {
width: 100%;
height: 100%;
}
.container_image {
position: absolute;
bottom: 214rpx;
left: 76rpx;
}
}
.bottom {
display: flex;
justify-content: space-evenly;
align-items: center;
position: absolute;
bottom: 0;
width: 750rpx;
height: 218rpx;
background: #FFFFFF;
border-radius: 40rpx 40rpx 0rpx 0rpx;
// border: 2rpx solid #FF583D;
}
</style>
components/poster/index 组件
<template>
<view class="poster_wrapper">
<slot name="header"></slot>
<!-- 要生成海报的图片 -->
<image :src="imageUrl" mode="aspectFill" :style="{width:imageWidth + 'rpx'}" @click="click" class="imagebig"></image>
<!-- 这里把canvas移到了屏幕外面,如果需要查看canvas的话把定位去掉 -->
<!-- position:'fixed',left:'9999px',top:'0' -->
<canvas :style="{width:canvasWidth + 'px',height:canvasHeight + 'px',position:'fixed',left:'9999px',top:'0'}"
canvas-id="myCanvas" id="myCanvas" class="canvas"></canvas>
<!-- 遮罩层 -->
<!-- <view class="mask" v-if="showMask" @click="hideMask"> -->
<!-- 生成的海报图 -->
<!-- <image :style="posterSize" :src="lastPoster" :mode="config.imageMode" @click.stop=""></image> -->
<!-- <view class="btn_wrapper" @click.stop>
<slot name="save">
<button type="primary" @click="saveToAlbum">保存至相册</button>
</slot>
</view> -->
<!-- </view> -->
</view>
</template>
<script>
import {
loadImage,
createPoster,
canvasToTempFilePath,
saveImageToPhotosAlbum
} from '@/utils/poster.js';
import {
getWechatCode
} from "@/utils/appletCode.js";
export default {
props: {
// 展示图片的宽 单位 rpx
imageWidth: {
type: [String, Number],
default: 550
},
// 展示图片的高 单位 rpx
imageHeight: {
type: [String, Number],
default:980
},
// 展示图片的url
imageUrl: {
type: String,
default: '',
required: true
},
// 绘制海报的数据参数
// drawData: {
// type: Array,
// default: () => ([]),
// required: true
// },
// 海报的配置参数
config: {
type: Object,
default: () => ({
imageMode: 'aspectFit',
posterHeight:1000,
}),
},
// 是否需要小程序二维码
wechatCode: {
type: Boolean,
default: false
},
// 小程序二维码的配置参数
wechatCodeConfig: {
type: Object,
default: () => ({
serverUrl: '',
scene: '',
config: {
x: 0,
y: 0,
w: 100,
h: 100
}
}),
}
},
data() {
return {
// 资源是否加载成功的标志
readyed: false,
// 将网络图片转成静态图片后的绘制参数
imageMap: [],
// 最后生成的海报的本地缓存地址
lastPoster: '',
// 是否展示遮罩
showMask: false,
// 是否加载资源的标志
loadingShow: false,
// 是否可以创建海报
disableCreatePoster:false,
drawData :[
{
type: 'image',
config: {
url: '',
x: 0,
y: 0,
w: 275,
h: 490
},
},
{
type: 'image',
config: {
url: '',
x: 40,
y: 380,
w: 40,
h: 40
},
},
{
type: 'text',
config: {
text: '',
x: 140,
y: 60,
color: '#E60012',
font: 'normal bold 16px 仿宋',
textAlign: 'center'
}
}
],
}
},
computed: {
// 所生成海报图的大小
posterSize() {
let str = '';
this.config.posterWidth && (str += `width:${this.config.posterWidth};`);
this.config.posterHeight && (str += `height:${this.config.posterHeight};`);
return str
},
// 画布的宽,优先使用配置的宽,如果没用配置默认使用图片的宽
// 需要主要的是这里canvas和image的单位不同,不过并不影响
// 我们在绘制时(配置drawData)以px为基准进行绘制就行,用px的原因时防止不同设备Dpr不同导致无法确定画布的具体宽高,使得最后的图片可能会留白边
canvasWidth(){
return this.config.canvasWidth ? this.config.canvasWidth : this.imageWidth
},
// 画布的高,优先使用配置的高,如果没用配置默认使用图片的高
canvasHeight(){
return this.config.convasHeight ? this.config.convasHeight : this.imageHeight
}
},
watch: {
// 监听外部绘制参数的变化,重新加载资源
// drawData(newVlaue) {
// this.loadingResources(newVlaue)
// },
// 监听readyed变化
// readyed(newVlaue) {
// if (newVlaue == true && this.loadingShow == true) {
// uni.hideLoading()
// this.loadingShow = false;
// this.createImage();
// }
// }
// 会存在异步问题,还没解决。
// 目前的解决方法 1.在绘制之前先改变 scene 2.改变scene后手动调用this.loadingResources 函数,但是资源会重新加载
// "wechatCodeConfig.scene":function (newVlaue){
// console.log('wechatCodeConfig.scene',this.imageMap)
// this.loadingWechatCode(this.imageMap)
// }
},
created() {
this.usercode()
// this.$emit('click')
// this.loadingResources(this.drawData)
},
methods: {
cjian(){
this.usercode()
},
//二维码
usercode() {
let $this=this
uni.request({
url: "xxxxxxx?m=user&c=indexapi&a=affiliate",
method: 'POST',
header: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: {
"user_id": parseInt(uni.getStorageSync('USER_ID'))
},
success(res) {
console.log(res.data.data,'数据');
$this.drawData=[{
type: 'image',
config: {
url: 'xxxxxxx/static/poster/%E7%BC%96%E7%BB%84%205%403x.png',
x: 0,
y: 0,
w: 275,
h: 490
},
},
{
type: 'image',
config: {
url: res.data.data,
x: 35,
y: 350,
w: 70,
h:70
},
},
{
type: 'text',
config: {
text: '',
x: 140,
y: 60,
color: '#E60012',
font: 'normal bold 16px 仿宋',
textAlign: 'center'
}
}
]
// $this.wechatCodeConfig.serverUrl=res.data.data
$this.loadingResources($this.drawData)
}
});
},
// 加载静态资源,创建或更新组件内所下载本地图片集合
async loadingResources(drawData) {
// this.readyed = false;
if (!drawData.length || drawData.length <= 0) return;
// 加载静态图片,将所以图片的网络地址替换成本地缓存地址
const tempMap = [];
for (let i = 0; i < drawData.length; i++) {
let temp
if (drawData[i].type === "image") {
temp = await loadImage(drawData[i].config.url);
drawData[i].config.url = temp;
}
tempMap.push({
...drawData[i],
url: temp
})
}
// 加载小程序二维码
await this.loadingWechatCode(tempMap);
// 赋值给imageMap保存
this.imageMap = tempMap;
setTimeout(() => {
// this.readyed = true;
this.createImage()
}, 100)
},
// 绘制海报图
async createImage() {
// console.log('点击执行',this.imageMap)
// 禁用生成海报,直接返回
// if(this.disableCreatePoster) return
// this.disableCreatePoster = true;
try {
// if (!this.readyed) {
// uni.showLoading({
// title: '静态资源加载中...'
// });
// this.loadingShow = true;
// this.$emit('loading')
// return
// }
// 获取上下文对象,组件内一定要传this
const ctx = uni.createCanvasContext('myCanvas', this);
await createPoster(ctx, this.imageMap);
this.lastPoster = await canvasToTempFilePath('myCanvas', this);
this.showMask = true;
console.log('点击执行',this.imageMap,this.lastPoster)
// this.disableCreatePoster = false;
// 创建成功函数
this.$emit('success',this.lastPoster)
} catch (e) {
// 创建失败函数
this.disableCreatePoster = false;
this.$emit('fail', e)
}
},
// 加载或更新小程序二维码
async loadingWechatCode(tempMap) {
if (this.wechatCode) {
if (this.wechatCodeConfig.serverUrl) {
const code = await getWechatCode(this.wechatCodeConfig.serverUrl, this.wechatCodeConfig.scene || '');
// 记录替换的索引,没有就替换length位,即最后加一个
let targetIndex = tempMap.length;
for (let i = 0; i < tempMap.length; i++) {
if (tempMap[i].wechatCode) targetIndex = i;
}
tempMap.splice(targetIndex, 1, {
type: 'image',
url: code.path,
// 标记是小程序二维码
wechatCode: true,
config: this.wechatCodeConfig.config,
})
} else {
throw new Error('serverUrl请求二维码服务器地址不能为空')
}
}
return tempMap
},
// 保存到相册
saveToAlbum() {
saveImageToPhotosAlbum(this.lastPoster).then(res => {
this.showMask = false;
uni.showToast({
icon: 'none',
title: '保存成功'
})
}).catch(err => {
})
},
click() {
this.$emit('click')
this.showMask = false;
this.$emit('hidemask')
},
hideMask(){
this.showMask = false;
this.$emit('hidemask')
}
},
}
</script>
<style scoped>
.poster_wrapper {
width: 100%;
height:100vh;
display: flex;
flex-direction: column;
align-items: center;
}
.imagebig{
height:100%;
}
.canvas {
border: 1px solid #333333;
}
.mask {
width: 100vw;
height: 100vh;
position: fixed;
background-color: rgba(0,0,0,.4);
left: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
</style>
@/utils/poster.js
// 错误提示集合
const errMsgMap = {
'arc': {
'x': '请指定圆的起始位置 x',
'y': '请指定圆的起始位置 y',
'r': '请指定圆的半径 r',
'sAngle': '请指定圆的起始弧度 sAngle',
'eAngle': '请指定圆的终止弧度 eAngle',
},
'rect': {
'x': '请指定矩形的起始位置 x',
'y': '请指定矩形的起始位置 y',
'w': '请指定矩形的宽度 w',
'h': '请指定矩形的高度 h',
},
'round_rect': {
'x': '请指定矩形边框的起始位置 x',
'y': '请指定矩形边框的起始位置 y',
'w': '请指定矩形边框的宽度 w',
'h': '请指定矩形边框的高度 h',
},
'stroke_rect': {
'x': '请指定矩形边框的起始位置 x',
'y': '请指定矩形边框的起始位置 y',
'w': '请指定矩形边框的宽度 w',
'h': '请指定矩形边框的高度 h',
},
'stroke_round_rect': {
'x': '请指定矩形边框的起始位置 x',
'y': '请指定矩形边框的起始位置 y',
'w': '请指定矩形边框的宽度 w',
'h': '请指定矩形边框的高度 h',
},
'text': {
'x': '请指定文本的起始位置 x',
'y': '请指定文本的起始位置 y',
'text': '请指定文本的内容 text'
},
'image': {
'x': '请指定图片的起始位置 x',
'y': '请指定图片的起始位置 y',
'w': '请指定图片的宽度 w',
'h': '请指定图片的高度 h',
'url': '请指定图片的路径 url'
},
'line': {
'path': '请指定线的路径 path'
},
'points': {
'points': '请指定点集合 points'
}
};
// 绘制方法集合
const DrawFuncMap = {
drawLine(ctx, config, i, isClip) {
// clip 图形默认不需要 fill 和 stroke 颜色。
const defaultColor = isClip ? 'transparent' : '#333333'
// 检验必传参数
checkNecessaryParam(config, 'line', i, 'path');
// 每一个path就描述了一组线的开始到结束,这一组线段不一定是连续的,根据配置属性来具体描述这个线
// 他们的形态是一样的(线的粗细,颜色),形状不一样且不一定是连续的
for (let j = 0; j < config.path.length; j++) {
const path = config.path[j];
checkNecessaryParam(path, 'points', `${i}-${j}`, 'points');
const lineWidth = path.lineWidth || 1;
const lineJoin = path.lineJoin || 'round';
const lineCap = path.lineCap || 'round';
// ctx.beginPath();
// 设置颜色
ctx.setStrokeStyle(path.strokeStyle || defaultColor);
// 设置填充色
ctx.setFillStyle(path.fillStyle || defaultColor);
// 设置粗细
ctx.setLineWidth(lineWidth);
// 设置线条交点样式
ctx.setLineJoin(lineJoin);
// 设置线条的断点样式
ctx.setLineCap(lineCap);
// 遍历线的点集合,根据每个点的不同属性来绘制成线
for (let k = 0; k < path.points.length; k++) {
// 拿到每一个点
const pointSet = path.points[k];
// 如果该点是一个数组集合,则点的类型直接当 lineTo 处理
if (Object.prototype.toString.call(pointSet) === "[object Array]") {
if (k === 0) ctx.moveTo(...pointSet);
else ctx.lineTo(...pointSet);
} else {
// 默认的第一个点一定是起始点,且点类型为 moveTo 则执行 ctx.moveTo 移动画笔
if (k === 0 || pointSet.type === 'moveTo') {
ctx.moveTo(...pointSet.point);
// 点的类型为 lineTo 或 没有 type 属性也默认为 lineTo 至执行 ctx.lineTo 连线
} else if (pointSet.type === 'lineTo' || pointSet.type === undefined) {
ctx.lineTo(...pointSet.point);
} else if (pointSet.type === 'bezierCurveTo') {
const P2 = pointSet.P2 ? pointSet.P2 : pointSet.P1;
ctx.bezierCurveTo(...pointSet.P1, ...P2, ...pointSet.point);
} else if (pointSet.type === 'closePath') {
ctx.closePath()
ctx.fill();
}
}
}
// 每一组点集合(path)结束 stroke
ctx.stroke();
}
},
// 绘制图片
drawImage(ctx, config, i) {
checkNecessaryParam(config, 'image', i, 'x', 'y', 'w', 'h', 'url');
ctx.drawImage(config.url, config.x, config.y, config.w, config.h);
},
// 绘制圆
drawArc(ctx, config, i, isClip) {
const defaultColor = isClip ? 'transparent' : '#333333'
checkNecessaryParam(config, 'arc', i, 'x', 'y', 'r', 'sAngle', 'eAngle');
// ctx.beginPath();
ctx.arc(config.x, config.y, config.r, config.sAngle, config.eAngle);
ctx.setFillStyle(config.fillStyle || defaultColor);
ctx.fill();
ctx.setLineWidth(config.lineWidth || 1);
ctx.setStrokeStyle(config.strokeStyle || defaultColor);
ctx.stroke();
},
// 绘制文字
drawText(ctx, config, i) {
checkNecessaryParam(config, 'text', i, 'x', 'y', 'text');
ctx.font = config.font || '10px sans-serif';
ctx.setFillStyle(config.color || '#333333');
ctx.setTextAlign(config.textAlign || 'center');
ctx.fillText(config.text, config.x, config.y);
ctx.stroke();
},
// 绘制矩形
drawRect(ctx, config, i, isClip) {
const defaultColor = isClip ? 'transparent' : '#333333'
checkNecessaryParam(config, 'rect', i, 'x', 'y', 'w', 'h');
// ctx.beginPath();
ctx.rect(config.x, config.y, config.w, config.h);
ctx.setFillStyle(config.fillStyle || defaultColor);
ctx.setLineWidth(config.lineWidth || 1);
ctx.setStrokeStyle(config.strokeStyle || defaultColor);
ctx.stroke();
ctx.fill();
},
// 绘制非填充矩形
drawStrokeRect(ctx, config, i, isClip) {
checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
// ctx.beginPath();
ctx.setStrokeStyle(config.strokeStyle || '#333333');
ctx.setLineWidth(config.lineWidth || 1);
ctx.strokeRect(config.x, config.y, config.w, config.h);
ctx.stroke();
},
// 绘制填充圆角矩形
drawRoundRect(ctx, config, i, isClip) {
// 当为裁剪图形,需要把 fill 和 stroke 颜色设置为透明
const defaultColor = isClip ? 'transparent' : '#333333'
checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
ctx.setFillStyle(config.fillStyle || defaultColor);
this.drawRoundRectPath(ctx, config.x, config.y, config.w, config.h, config.r)
ctx.fill();
},
// 绘制非填充圆角矩形
drawStrokeRoundRect(ctx, config, i, isClip) {
checkNecessaryParam(config, 'stroke_rect', i, 'x', 'y', 'w', 'h');
ctx.setStrokeStyle(config.strokeStyle || '#333333');
ctx.setLineWidth(config.lineWidth || 1);
this.drawRoundRectPath(ctx, config.x, config.y, config.w, config.h, config.r)
ctx.stroke();
},
// 绘制圆角矩形路径
drawRoundRectPath(ctx, x, y, w, h, r) {
// ctx.beginPath();
// ctx.strokeRect(config.x, config.y, config.w, config.h);
//从右下角顺时针绘制,弧度从0到1/2PI
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2);
//矩形下边线
ctx.lineTo(x + r, y + h);
//左下角圆弧,弧度从1/2PI到PI
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI);
//矩形左边线
ctx.lineTo(x, y + r);
//左上角圆弧,弧度从PI到3/2PI
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2);
//上边线
ctx.lineTo(x + w - r, y);
//右上角圆弧
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, Math.PI * 2);
//右边线
ctx.lineTo(x + w, y + h - r);
ctx.closePath();
}
}
/**
* 检测绘制的必要属性
* @param {Object} configObj 配置对象
* @param {String} type 对应校验的类型
* @param {String|Number} index 当前的错误位置 从0开始对应绘画(drawData)配置中的索引,
* 当为 String 类型时会以'-'间隔出第几层的第几个,如1-2 表示是绘画(drawData)配置中第一个配置里的第二个子配置对象有问题,依次类推
* @param {Array} keyArr 搜集到的所以需要进行检验的键名
**/
function checkNecessaryParam(configObj, type, index, ...keyArr) {
// 这里要注意由于,绘画配置有些参数可能会漏写,所以 errMsgMap[type] 作为遍历对象进行比较
for (let prop in errMsgMap[type]) {
if (configObj[prop] === undefined) {
throw new Error(`第${index}顺位:${errMsgMap[type][prop]}`)
}
}
}
/**
* 根据不同的图形类型绘制图形
* @param {String} type 图形的类型
* @param {Object} ctx 当前 canvas 的上下文对象
* @param {Object} config 图形对应的配置属性对象,
* @param {String|Number} index 从0开始对应绘画(drawData)配置中的索引
* @param {Boolean} isClip 是否应用于剪切
**/
function drawFigureByType(type, ctx, config, index, isClip) {
if (type === 'image') {
!isClip && DrawFuncMap.drawImage(ctx, config, index);
} else if (type === 'text') {
!isClip && DrawFuncMap.drawText(ctx, config, index);
} else if (type === 'arc') {
DrawFuncMap.drawArc(ctx, config, index, isClip);
} else if (type === 'rect') {
DrawFuncMap.drawRect(ctx, config, index, isClip);
} else if (type === 'stroke_rect') {
// 这里非填充矩形也按照矩形的方式绘制裁剪区域
isClip ? DrawFuncMap.drawRect(ctx, config, index, isClip) : DrawFuncMap.drawStrokeRect(ctx, config, index,
isClip);
} else if (type === 'stroke_round_rect') {
// 这里非填充圆角矩形也按照圆角矩形的方式绘制裁剪区域
isClip ? DrawFuncMap.drawRoundRect(ctx, config, index, isClip) : DrawFuncMap.drawStrokeRoundRect(ctx, config,
index, isClip);
} else if (type === 'round_rect') {
DrawFuncMap.drawRoundRect(ctx, config, index, isClip);
} else if (type === 'line') {
DrawFuncMap.drawLine(ctx, config, index, isClip)
}
}
/**
* 不同类型的图形都可以绘制不同的剪切图形
* @param {String} type 绘制图形的类型
* @param {Object} ctx 当前 canvas 的上下文对象
* @param {Object} config 绘制图形对应的配置属性对象,
* @param {String|Number} index 绘制图形的索引 从0开始对应绘画(drawData)配置中的索引
* @param {String} clipType 剪切图形的类型
* @param {Object} clipConfig 剪切图形对应的配置属性对象,
**/
function drawClipFigure(type, ctx, config, index, clipType, clipConfig) {
ctx.beginPath();
drawFigureByType(clipType, ctx, clipConfig, index, true)
ctx.clip()
ctx.beginPath();
drawFigureByType(type, ctx, config, index, false)
}
// 获取图片信息,这里主要要获取图片缓存地址
export function loadImage(url) {
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success(res) {
resolve(res.path)
},
fail(err) {
reject('海报图资源加载失败')
}
})
})
}
// 解析海报对象,绘制canvas海报
export function createPoster(ctx, posterItemList) {
return new Promise((resolve, reject) => {
try {
for (let i = 0; i < posterItemList.length; i++) {
const temp = posterItemList[i];
// 如果有 clip 属性需要先建立 clip 剪切区域
if (temp.clip) {
ctx.save()
// 绘制剪切图形
drawClipFigure(temp.type, ctx, temp.config, i, temp.clip.type, temp.clip.config)
ctx.restore()
} else {
ctx.beginPath();
//正常绘制图形
drawFigureByType(temp.type, ctx, temp.config, i, false)
}
}
ctx.draw();
resolve({
result: 'ok',
msg: '绘制成功'
})
} catch (e) {
console.error(e)
reject({
result: 'fail',
msg: e
})
}
})
}
// canvas转image图片
export function canvasToTempFilePath(canvasId, vm, delay = 50) {
return new Promise((resolve, reject) => {
// 这里canvas绘制完成之后想要存缓存需要一定时间,这里设置了50毫秒
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: canvasId,
x:0,
y:0,
width: 300,
height: 490,
destWidth: 300,
destHeight:490,
success(res) {
if (res.errMsg && res.errMsg.indexOf('ok') != -1) resolve(res.tempFilePath);
else reject(res)
},
fail(err) {
reject(err)
}
}, vm);
}, delay)
})
}
// 保存图片到相册
export function saveImageToPhotosAlbum(imagePath) {
return new Promise((resolve, reject) => {
uni.saveImageToPhotosAlbum({
filePath: imagePath,
success(res) {
resolve(res)
},
fail(err) {
reject(err)
}
})
})
}
@/utils/appletCode.js
import {base64src} from "@/utils/base64src.js";
/**
* 微信获取小程序二维码
* @param {String} url 微信服务器地址
* @param {String} scene 二维码所携带的信息
* @return {Object} 返回的二维码对象
**/
export function getWechatCode (url,scene) {
return new Promise((resolve,reject)=>{
wx.request({
url: url,
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
// 二维码携带的信息
data: {
scene: scene
},
success(res) {
//将base64图片转换成本地路径
base64src("data:image/PNG;base64," + res.data.qcode, res => {
// 获取图片信息
wx.getImageInfo({
src: res,
success(res) {
resolve(res);
},
fail(err) {
reject(err);
}
})
})
},
fail(err){
reject(err);
}
})
})
}