Files
jwl-applet/src/pages/index/components/ggl/scraping.vue
2024-01-27 21:21:24 +08:00

280 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="scraping-happy" id="container">
<canvas canvas-id="scraping-happy" class="scraping__canvas" :disable-scroll="true" @touchstart="touchstart"
@touchmove="touchmove" @touchend="touchend" />
<cover-view class="scraping__view">
{{ showText }}
</cover-view>
<slot></slot>
</view>
</template>
<script>
/** @name 水印配置默认值 **/
const WATERMARK = {
text: '刮一刮',
fontSize: 14,
color: '#C5C5C5',
}
/** @name 标题配置默认值 **/
const TITLE = {
text: '刮一刮',
fontSize: 16,
color: '#333',
}
/**
* @name 涂层配置默认值
* @property { string } color 涂层颜色
* @property { number } drawSize 清除涂层的画笔大小
*/
const MASK = {
color: '#DDDDDD',
drawSize: 20,
}
/** @name 容错值解决部分机型涂层没有覆盖满的情况主要原因是由于像素尺寸不同导致的应尽可能让width与height保持整数 **/
const TOLERANT = 3;
let ctx = null;
export default {
props: {
/** @name 涂层设置 **/
mask: {
type: [String, Object],
},
/** @name 水印设置 **/
watermark: {
type: [String, Object],
},
/** @name 提示文字 **/
title: {
type: [String, Object],
},
/** @name 刮开百分之多少直接消除图层为0的时候不消除 **/
percentage: {
type: Number,
default: 40,
},
result: {
type: String,
default: ''
}
},
data() {
return {
width: 0,
height: 0,
touchX: 0,
touchY: 0,
showText: ''
}
},
computed: {
maskSetting() {
return {
...MASK,
...(typeof this.mask === 'object' ? this.mask : {
text: this.mask
}),
}
},
watermarkSetting() {
return {
...WATERMARK,
...(typeof this.watermark === 'object' ? this.watermark : {
text: this.watermark
}),
};
},
titleSetting() {
return {
...TITLE,
...(typeof this.title === 'object' ? this.title : {
text: this.title
}),
};
}
},
mounted() {
// 获取画布实例
ctx = uni.createCanvasContext('scraping-happy', this);
this.init();
},
methods: {
/** @name 初始化 **/
init() {
const query = uni.createSelectorQuery().in(this);
query
.select('#container')
.boundingClientRect(({
width,
height
}) => {
this.width = width;
this.height = height;
setTimeout(() => {
this.initCanvas();
this.showText = this.result
}, 20)
})
.exec();
},
/** @name 初始化canvas状态 **/
initCanvas() {
const {
width,
height
} = this;
// 清空矩形内容
ctx.clearRect(0, 0, width, height);
// 设置画笔颜色
ctx.setFillStyle(this.maskSetting.color);
// 绘制矩形
ctx.fillRect(0, 0, width, height + TOLERANT);
// 绘制水印
this.drawWatermark();
// 绘制提示文字
this.drawTitle();
// 绘制到canvas身上
ctx.draw();
},
/** @name 绘制水印 **/
drawWatermark() {
if (!this.watermarkSetting.text) return;
// 保存当前的绘图上下文
ctx.save();
// 旋转
ctx.rotate((-10 * Math.PI) / 180);
// 水印具体绘制过程
const {
width,
height
} = this;
const watermarkWidth = this.watermarkSetting.text.length * this.watermarkSetting.fontSize;
let x = 0;
let y = 0;
let i = 0;
while ((x <= width * 5 || y <= height * 5) && i < 300) {
ctx.setFillStyle(this.watermarkSetting.color);
ctx.setFontSize(this.watermarkSetting.fontSize);
ctx.fillText(this.watermarkSetting.text, x, y);
x += watermarkWidth + watermarkWidth * 1.6;
if (x > width && y <= height) {
x = -Math.random() * 100;
y += this.watermarkSetting.fontSize * 3;
}
i++;
}
ctx.restore();
},
/** @name 绘制提示文字 **/
drawTitle() {
if (!this.titleSetting.text) return;
ctx.setTextAlign('center');
ctx.setTextBaseline('middle');
ctx.setFillStyle(this.titleSetting.color);
ctx.setFontSize(this.titleSetting.fontSize);
ctx.fillText(this.titleSetting.text, this.width / 2, this.height / 3);
},
/** @name 触摸事件 **/
touchstart(e) {
this.touchX = e.touches[0].x;
this.touchY = e.touches[0].y;
},
async touchmove(e) {
// 把画笔到画布中的指定点
ctx.moveTo(this.touchX, this.touchY);
// 清除涂层
ctx.clearRect(this.touchX, this.touchY, this.maskSetting.drawSize, this.maskSetting.drawSize);
ctx.draw(true);
// 记录移动点位
this.touchX = e.touches[0].x;
this.touchY = e.touches[0].y;
// if (this.percentage > 0) {
// const clearPercent = await this.getClearMaskPercent();
// }
},
async touchend() {
if (this.percentage > 0) {
const clearPercent = await this.getClearMaskPercent();
if (clearPercent >= this.percentage) {
ctx.moveTo(0, 0);
ctx.clearRect(0, 0, this.width, this.height);
ctx.stroke();
ctx.draw(true);
this.$emit('complete')
}
}
},
/** @name 计算被清除的涂层百分比 **/
getClearMaskPercent() {
return new Promise(resolve => {
uni.canvasGetImageData({
canvasId: 'scraping-happy',
x: 0,
y: 0,
width: this.width,
height: this.height,
success: res => {
// 区域内所有点的像素信息,它是一个数组,数组中每 "4" 项表示一个点的 rgba 值
const allPointPixels = res.data;
// 储存被清除的点-点的透明度
const clearPoint = [];
// 取透明度来判断,如果透明度值小于一半,则判断为该点已经被清除
for (let i = 0; i < allPointPixels.length; i += 4) {
if (allPointPixels[i + 3] < 128) {
clearPoint.push(allPointPixels[i + 3]);
}
}
// 已被清除的百分比 = 清除的点 / 全部的点
const percent = (
(clearPoint.length / (allPointPixels.length / 4)) *
100
).toFixed(2);
resolve(percent);
},
fail: e => {
console.log('canvasGetImageData', e);
},
}, this);
});
},
/** @name 重置 **/
reset() {
this.initCanvas();
this.touchX = 0;
this.touchY = 0;
}
}
};
</script>
<style>
.scraping-happy {
width: 100%;
height: 200rpx;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.scraping__canvas {
width: 100%;
height: 100%;
position: absolute;
z-index: 10;
/* background-color: red; */
display: inline-block;
}
.scraping__view {
position: absolute;
z-index: 1;
color: #f29100;
font-size: 20px;
font-weight: bold;
letter-spacing: 8px;
}
</style>