iot-ui-app/components/hch-poster/hch-poster.vue

422 lines
11 KiB
Vue

<!--
* @Description: 生成海报组件
* @Version: 1.0.0
* @Autor: hch
* @Date: 2020-08-07 14:48:41
* @LastEditors: Please set LastEditors
* @LastEditTime: 2021-07-31 18:11:35
* 保存海报按钮和关闭按钮 在html代码中写出来 绑定点击方法然后透明 再用canvas 覆盖
-->
<template>
<view
class="canvas-content"
v-show="canvasShow"
:style="'width:' + system.w + 'px; height:' + system.h + 'px;'"
>
<!-- 遮罩层 -->
<view class="canvas-mask"></view>
<!-- 海报 -->
<!-- :width="system.w" :height="system.h" 支付宝必须要这样设置宽高才有效果 -->
<canvas
class="canvas"
canvas-id="myCanvas"
id="myCanvas"
:style="'width:' + system.w + 'px; height:' + system.h + 'px;'"
:width="system.w"
:height="system.h"
></canvas>
<view class="button-wrapper">
<!-- 保存海报按钮 -->
<!-- #ifndef MP-QQ -->
<!-- cover-view 标签qq小程序有问题 -->
<view class="save-btn" @tap="handleSaveCanvasImage">保存海报</view>
<view class="save-btn cancel-btn" @tap="handleCanvasCancel">取消</view>
<!-- #endif -->
<!-- #ifdef MP-QQ -->
<view class="save-btn" @tap="handleSaveCanvasImage">保存海报</view>
<view class="save-btn cancel-btn" @tap="handleCanvasCancel">取消</view>
<!-- #endif -->
</view>
</view>
</template>
<script>
import { drawSquarePic, drawTextReturnH, getSystem } from './utils'
export default {
data() {
return {
system: {},
canvasShow: false
}
},
props: {
posterData: {
type: Object,
default: () => {
return {}
}
}
},
computed: {
/**
* @description: 计算海报背景数据
* @param {*}
* @return {*}
* @author: hch
*/
poster() {
let data = this.posterData
let system = this.system
let posterBg = {
url: data.poster.url,
r: data.poster.r * system.scale,
w: data.poster.w * system.scale,
h: data.poster.h * system.scale,
x: (system.w - data.poster.w * system.scale) / 2,
y: (system.h - data.poster.h * system.scale) / 2,
p: data.poster.p * system.scale
}
return posterBg
},
/**
* @description: 计算海报头部主图
* @param {*}
* @return {*}
* @author: hch
*/
mainImg() {
let data = this.posterData
let system = this.system
let posterMain = {
url: data.mainImg.url,
r: data.mainImg.r * system.scale,
w: data.mainImg.w * system.scale,
h: data.mainImg.h * system.scale,
x: (system.w - data.mainImg.w * system.scale) / 2,
y: this.poster.y + data.poster.p * system.scale
}
return posterMain
},
/**
* @description: 计算海报标题
* @param {*}
* @return {*}
* @author: hch
*/
title() {
let data = this.posterData
let system = this.system
let posterTitle = data.title
posterTitle.x = this.mainImg.x
posterTitle.y = this.mainImg.y + this.mainImg.h + data.title.mt * system.scale
return posterTitle
},
/**
* @description: 计算小程序码
* @param {*}
* @return {*}
* @author: hch
*/
codeImg() {
let data = this.posterData
let system = this.system
let posterCode = {
url: data.codeImg.url,
r: data.codeImg.r * system.scale,
w: data.codeImg.w * system.scale,
h: data.codeImg.h * system.scale,
x: this.mainImg.x,
y: data.codeImg.mt * system.scale //y需要加上绘图后文本的y
}
return posterCode
}
},
created() {
// 获取设备信息
this.system = getSystem()
},
methods: {
/**
* @description: 展示海报
* @param {type}
* @return {type}
* @author: hch
*/
posterShow() {
console.log("海报生成软件获取到数据为",this.posterData)
this.canvasShow = true
this.creatPoster()
},
/**
* @description: 生成海报
* @author: hch
*/
async creatPoster() {
uni.showLoading({
title: '生成海报中...'
})
const ctx = uni.createCanvasContext('myCanvas', this)
this.ctx = ctx
ctx.clearRect(0, 0, this.system.w, this.system.h) //清空之前的海报
ctx.draw() //清空之前的海报
// 根据设备屏幕大小和距离屏幕上下左右距离,及圆角绘制背景
let poster = this.poster
let mainImg = this.mainImg
let codeImg = this.codeImg
let title = this.title
console.log("生成海报之前获取的数据为",mainImg)
await drawSquarePic(ctx, poster.x, poster.y, poster.w, poster.h, poster.r, poster.url)
await drawSquarePic(ctx, mainImg.x, mainImg.y, mainImg.w, mainImg.h, mainImg.r, mainImg.url)
// 绘制标题 textY 绘制文本的y位置
console.log('creatPoster -> title.x', title.x)
let textY = drawTextReturnH(
ctx,
title.text,
title.x,
title.y,
mainImg.w,
title.fontSize,
title.color,
title.lineHeight,
title.textAlign
)
console.log("绘制小程序码codeImg",codeImg)
// 绘制小程序码
await drawSquarePic(
ctx,
codeImg.x,
codeImg.y + textY,
codeImg.w,
codeImg.h,
codeImg.r,
codeImg.url
)
// 小程序的名称
// 长按/扫描识别查看商品
let y = 0
let x = codeImg.x + codeImg.w + 90
this.posterData.tips.forEach((element, i) => {
if (i == 0) {
y = codeImg.y + textY + codeImg.h/2 - 12
} else {
y += element.mt
}
y = drawTextReturnH(
ctx,
element.text,
x,
y,
mainImg.w,
element.fontSize,
element.color,
element.lineHeight,
element.align
)
})
uni.hideLoading()
},
/**
* @description: 保存到系统相册
* @param {type}
* @return {type}
* @author: hch
*/
handleSaveCanvasImage() {
uni.showLoading({
title: '保存中...'
})
let _this = this
// 把画布转化成临时文件
// #ifndef MP-ALIPAY
// 支付宝小程序外,其他都是用这个方法 canvasToTempFilePath
uni.canvasToTempFilePath(
{
x: this.poster.x,
y: this.poster.y,
width: this.poster.w, // 画布的宽
height: this.poster.h, // 画布的高
destWidth: this.poster.w * 5,
destHeight: this.poster.h * 5,
canvasId: 'myCanvas',
success(res) {
//保存图片至相册
// #ifndef H5
// 除了h5以外的其他端
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
uni.hideLoading()
uni.showToast({
title: '图片保存成功,可以去分享啦~',
duration: 2000,
icon: 'none'
})
_this.handleCanvasCancel()
},
fail() {
uni.showToast({
title: '保存失败,稍后再试',
duration: 2000,
icon: 'none'
})
uni.hideLoading()
}
})
// #endif
// #ifdef H5
// h5的时候
uni.showToast({
title: '请长按保存',
duration: 3000,
icon: 'none'
})
_this.handleCanvasCancel()
_this.$emit('previewImage', res.tempFilePath)
// #endif
},
fail(res) {
console.log('fail -> res', res)
uni.showToast({
title: '保存失败,稍后再试',
duration: 2000,
icon: 'none'
})
uni.hideLoading()
}
},
this
)
// #endif
// #ifdef MP-ALIPAY
// 支付宝小程序条件下 toTempFilePath
this.ctx.toTempFilePath(
{
x: this.poster.x,
y: this.poster.y,
width: this.poster.w, // 画布的宽
height: this.poster.h, // 画布的高
destWidth: this.poster.w * 5,
destHeight: this.poster.h * 5,
success(res) {
//保存图片至相册
my.saveImage({
url: res.apFilePath,
showActionSheet: true,
success(res) {
uni.hideLoading()
uni.showToast({
title: '图片保存成功,可以去分享啦~',
duration: 2000,
icon: 'none'
})
_this.handleCanvasCancel()
},
fail() {
uni.showToast({
title: '保存失败,稍后再试',
duration: 2000,
icon: 'none'
})
uni.hideLoading()
}
})
},
fail(res) {
console.log('fail -> res', res)
uni.showToast({
title: '保存失败,稍后再试',
duration: 2000,
icon: 'none'
})
uni.hideLoading()
}
},
this
)
// #endif
},
/**
* @description: 取消海报
* @param {type}
* @return {type}
* @author: hch
*/
handleCanvasCancel() {
this.canvasShow = false
this.$emit('cancel', true)
}
}
}
</script>
<style lang="scss">
.content {
height: 100%;
text-align: center;
}
.canvas-content {
position: absolute;
top: 0;
.canvas-mask {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 998;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
}
.canvas {
z-index: 999;
}
.button-wrapper {
position: fixed;
bottom: 20rpx;
z-index: 999;
display: flex;
width: 100%;
height: 72rpx;
justify-content: space-around;
}
.save-btn {
z-index: 1000;
width: 40%;
height: 100%;
font-size: 30rpx;
line-height: 72rpx;
color: #fff;
text-align: center;
background: $uni-btn-color;
border-radius: 45rpx;
border-radius: 36rpx;
}
.cancel-btn {
color: $uni-btn-color;
background: #fff;
}
.canvas-close-btn {
position: fixed;
top: 30rpx;
right: 0;
z-index: 1000;
width: 60rpx;
height: 60rpx;
padding: 20rpx;
}
}
</style>