<view class="luck_canvas">
<view class="luck_info" v-for="(item, index) in cardList" :key="index">
<view class="luck_num">
{{item.num}}
</view>
<view class="luck_price">
{{item.gold}}金币
</view>
<!-- :style="{'width':'156rpx','height':'156rpx'}" -->
<!-- :style="{'width':width + 'px','height':height + 'px'}" -->
</view>
<canvas class="canvas_box" :style="{'width':'646rpx','height':'312rpx'}" canvas-id="myCanvas" id="myCanvas" @touchstart="touchstart" @touchend="touchend" @touchmove="touchmove">
</canvas>
</view>
// 参数
canvasCtx: null,
clearPercent: 0,
lastPoint: {},
disabled: false, // 是否禁止刮卡
readyState: false, // 是否开始绘制
endState: false, // 结束刮卡状态
startX: 0, // 触摸x轴位置
startY: 0, // 触摸y轴位置
touchSize: 20, // 触摸画笔大小
percentage: 50, // 刮开百分之多少的时候开奖
scratchList: [{
num: '08',
price: '0.01',
isShow: false
},
{
num: '25',
price: '0.01',
isShow: false
},
{
num: '30',
price: '0.01',
isShow: false
},
{
num: '19',
price: '0.01',
isShow: false
},
{
num: '01',
price: '0.01',
isShow: false
},
{
num: '08',
price: '0.01',
isShow: false
},
{
num: '55',
price: '0.01',
isShow: false
},
{
num: '39',
price: '0.01',
isShow: false
}
],
// 获取画布大小
getBoxInfo() {
this.$nextTick(() => {
let content = uni.createSelectorQuery().select(".luck_canvas");
content
.boundingClientRect((data) => {
console.log(data, "宽高");
this.width = data.width;
this.height = data.height;
setTimeout((e) => {
const canvasId = "myCanvas";
this.canvasCtx = uni.createCanvasContext(canvasId, this);
this.drawMask(this.canvasCtx, data.width, data.height);
// 页面加载后立即绘制遮挡图层
// this.scratchList.forEach((item, index) => {
// const canvasId = 'myCanvas' + index;
// this.ctxMap[canvasId] = uni.createCanvasContext(canvasId,
// this);
// this.drawMask(this.ctxMap[canvasId], data.width, data.height)
// });
}, 20);
})
.exec();
});
},
// 绘制遮挡图层
drawMask(ctx, width, height) {
// ctx.fillStyle = 'gray';
// ctx.fillRect(0, 0, width, height);
// ctx.draw(true);
// 假设有一个图片路径为 maskImagePath
const maskImagePath = "/static/glod_mask.png";
// 绘制图片作为遮挡图层
ctx.drawImage(maskImagePath, 0, 0, width, height);
ctx.draw(true);
},
// 手指触摸动作开始
touchstart(e) {
// if (this.disabled || this.endState) {
// return;
// }
if (this.cardState == 1) return;
this.isScroll = false;
if (e.touches && e.touches[0]) {
const point = e.touches[0];
this.lastPoint = point;
}
this.startX = e.touches[0].x;
this.startY = e.touches[0].y;
},
// 手指触摸后移动
touchmove(e) {
// e.preventDefault();
// const canvasId = e.currentTarget.id;
// if (this.disabled || this.endState) {
// return;
// }
if (this.cardState == 1) return;
const point = (e.changedTouches || e.touches || [])[0];
console.log(point, "move移动====");
if (point) {
this.refresh(point);
this.lastPoint = point;
}
return;
const ctx = this.canvasCtx;
if (ctx) {
// ctx.clearRect(this.startX, this.startY, this.touchSize, this.touchSize); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
// ctx.draw(true); // false:本次绘制是否接着上一次绘制,true:保留当前画布上的内容
ctx.beginPath();
ctx.arc(this.startX, this.startY, 20, 0, Math.PI * 2);
ctx.globalCompositeOperation = "destination-out"; // 设置混合模式为擦除模式
ctx.fill();
ctx.closePath();
ctx.draw(true); // false:本次绘制是否接着上一次绘制,true:保留当前画布上的内容
// this.getFilledPercentage()
}
// this[canvasId].clearRect(this.startX, this.startY, this.touchSize, this.touchSize); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
// this[canvasId].draw(true); // false:本次绘制是否接着上一次绘制,true:保留当前画布上的内容
// //记录移动点位
this.startX = e.touches[0].x;
this.startY = e.touches[0].y;
},
// 手指触摸动作结束
touchend(e) {
this.isScroll = true;
if (this.cardState == 1) return;
if (this.disabled || this.endState) {
return;
}
var point = (e.changedTouches || e.touches || [])[0];
if (!point) {
// 没有拿到point直接完成
this.success();
}
this.lastPoint = null;
if (this.clearPercent > 0.6) {
this.success();
}
return;
const canvasId = e.currentTarget.id;
// 返回一个数组,用来描述 canvas 区域隐含的像素数据,在自定义组件下,第二个参数传入自定义组件实例 this,以操作组件内 <canvas> 组件。
uni.canvasPutImageData(
{
canvasId: canvasId,
x: 0,
y: 0,
width: this.width,
height: this.height,
success: (res) => {
console.log(res);
let pixels = res.data;
let transPixels = [];
for (let i = 0; i < pixels.length; i += 4) {
if (pixels[i + 3] < 128) {
transPixels.push(pixels[i + 3]);
}
}
var percent = (
(transPixels.length / (pixels.length / 4)) *
100
).toFixed(2);
if (percent >= this.percentage) {
this.success();
}
},
fail: (e) => {
console.log(e);
},
},
this
);
},
refresh(point) {
// console.log(pointS,"pointS");
// var point = pointS.length > 0 && pointS[0] !== undefined ? pointS[0] : {};
/*
小程序 canvas 不支持 globalCompositeOperation 属性
this.ctx.globalCompositeOperation = "destination-out"; // 无效
所以要很 hack 的根据屏幕滑动始末两端点连成粗线条选区,再自己实现清除选区像素
*/
var pr = 1;
var ctx = this.canvasCtx;
var r = 20 / 2;
var x1 = this.lastPoint.x;
var y1 = this.lastPoint.y;
var x2 = point.x;
var y2 = point.y;
// (x1, y1), (x2, y2)分别为线条起始和结尾的两个端点,即粗线条两端点圆弧的圆心 矩形长为手指移动的线条长度,高为线条宽度lineWidth
// 获取两个点之间的剪辑区域四个端点,即矩形边框顶点(x3, y3)..(x6, y6)
var asin = r * Math.sin(Math.atan((y2 - y1) / (x2 - x1)));
var acos = r * Math.cos(Math.atan((y2 - y1) / (x2 - x1)));
var x3 = x1 + asin;
var y3 = y1 - acos;
var x4 = x1 - asin;
var y4 = y1 + acos;
var x5 = x2 + asin;
var y5 = y2 - acos;
var x6 = x2 - asin;
var y6 = y2 + acos;
// 保证线条的连贯,所以在矩形两端画圆
ctx.save();
ctx.beginPath();
ctx.arc(x1, y1, r, 0, 2 * Math.PI);
ctx.arc(x2, y2, r, 0, 2 * Math.PI);
ctx.clip();
ctx.clearRect(0, 0, this.width * pr, this.height * pr);
ctx.restore();
// 清除矩形剪辑区域里的像素
ctx.save();
ctx.beginPath();
ctx.moveTo(x3, y3);
ctx.lineTo(x5, y5);
ctx.lineTo(x6, y6);
ctx.lineTo(x4, y4);
ctx.closePath();
ctx.clip();
ctx.clearRect(0, 0, this.width * pr, this.height * pr);
ctx.restore();
// 清除线条像素方案2
// 在小程序内当滑动很快时会导致页面渲染崩溃白屏
// this._clearCircle(point, r);
// if (this.lastPoint) {
// let posX = point.x - this.lastPoint.x;
// let posY = point.y - this.lastPoint.y;
// let posXY = Math.abs(posX) + Math.abs(posY);
// while(posXY > 6) {
// Math.abs(posX) > 3 && (posX += (posX < 0 ? 3 : -3));
// Math.abs(posY) > 3 && (posY += (posY < 0 ? 3 : -3));
// this._clearCircle({x: point.x - posX, y: point.y - posY}, r);
// console.log(this.lastPoint, point, {x: point.x - posX, y: point.y - posY}, posX, posY)
// posXY = Math.abs(posX) + Math.abs(posY);
// }
// }
ctx.draw(true);
this.calculateClearPercent(x1, y1, x2, y2);
},
calculateClearPercent(x1, y1, x2, y2) {
let area = this.width * this.height;
console.log(x1, y1, x2, y2, typeof area, area);
var lx = x2 - x1;
var ly = y2 - y1;
var l = Math.sqrt(lx * lx + ly * ly);
console.log(l, "这是什么==", (l * 20) / area);
this.clearPercent += (l * 20) / area;
console.log(this.clearPercent, "this.clearPercent");
},
getFilledPercentage() {
console.log(this.canvasCtx, "this.canvasCtx");
let imgData = this.canvasCtx.getImageData(0, 0, this.width, this.height); //获取画布中的所有像素
console.log(imgData, "imgDataimgDataimgData");
let pixels = imgData.data; //得到像素的字节数据
let transparent = 0; //设置一个变量来记录已经变为透明的像素点的数量
for (let i = 0; i < pixels.length; i += 4) {
let alpha = pixels[i + 3]; //获取每个像素的透明度数值
if (alpha < 10) transparent++; //当透明度小于10时,认为它已经被擦除,transparent数值加1
}
let percentage = transparent / (pixels.length / 4); //计算透明像素在所有像素点中所占比例
if (percentage > 0.5) {
// 当像素点的个数超过 60% 时,清空画布,显示底图
this.canvasCtx.clearRect(0, 0, 250, 100);
}
},
// 成功,清除所有图层
success(e) {
if (this.cardState == 1) return;
this.canvasCtx.moveTo(0, 0); // 把路径移动到画布中的指定点,不创建线条。用 stroke() 方法来画线条。
this.canvasCtx.clearRect(0, 0, this.width, this.height); // 清除画布上在该矩形区域内的内容(x,y,宽,高)。
// this.canvasCtx.stroke(); // 画出当前路径的边框。默认颜色色为黑色。
this.canvasCtx.draw(true);
// this.cardState = 1
this.scrapingCard();
},
.luck_canvas {
border-radius: 16rpx;
display: flex;
flex-wrap: wrap;
position: relative;
.canvas_box {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 20;
}
.luck_info {
width: 159rpx;
height: 156rpx;
background: #fce9c4;
// border-radius: 16rpx 0rpx 0rpx 0rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-right: 2rpx dashed #ffe0a3;
border-bottom: 2rpx dashed #ffe0a3;
position: relative;
&:nth-child(n + 5) {
border-bottom: none !important;
}
&:nth-child(4n) {
border-right: none !important;
}
.luck_num {
font-family: PingFang SC, PingFang SC;
font-weight: 600;
font-size: 40rpx;
// color: #ff9b69;
// mix-blend-mode: difference;
/* 文字裁剪属性 */
/* 将文字颜色设置为透明色 */
// color: transparent;
text-transform: uppercase;
color: transparent;
-webkit-text-stroke: 1px #ff9b69;
}
.luck_price {
font-family: PingFang SC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #ff5963;
}
}
}
Comments | NOTHING