Appearance
点击五角星区域触发事件,五角星区域外不触发怎么实现?
最近遇到一个小问题,大屏首页出现了异形的点击按钮,几个六边形按钮组合在一起的场景。
但是在点击的时候会出现问题,因为DOM是个矩形,所以在点击区域外也会触发事件。
简单研究了一下,可以通过三种方案实现特殊区域的点击方法。
最后一种比较邪门!
SVG
通过SVG创建图形,将点击事件绑定在 polygon 元素上,实现特定区域的点击。
html
<div @click="handleArea">
<svg width="200" height="200" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<polygon
points="50,5 61,35 95,35 67,57 78,90 50,70 22,90 33,57 5,35 39,35"
fill="orange" @click.stop="handleInArea" />
</svg>
</div>
clip-path
通过CSS的 clip-path 属性对可视区域进行裁切,实现特定区域的点击。
html
<div @click="handleArea">
<div class="star" @click.stop="handleInArea"></div>
</div>
css
.star {
width: 200px;
height: 200px;
background-color: orange;
clip-path: polygon(
50% 5%,
61% 35%,
95% 35%,
67% 57%,
78% 90%,
50% 70%,
22% 90%,
33% 57%,
5% 35%,
39% 35%
);
}
RGBA判断
通过将页面转化为 canvas,获取点击区域像素点的 RGBA 色值的方式,判断点击区域是否为特定区域。
html
<div @click="handleGetRGB">
<div class="start2">
<div class="star-container">
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
</div>
</div>
<div style="margin-top: 20px">Js</div>
</div>
css
.start2 {
display: flex;
justify-content: center;
align-items: center;
height: 240px;
margin-top: -60px;
}
.star-container {
position: relative;
width: 200px;
height: 200px;
}
.star-arm {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-left: 30px solid transparent; /* 加宽角的宽度 */
border-right: 30px solid transparent;
border-bottom: 100px solid orange; /* 增加高度,控制五角星的纵向尺寸 */
transform-origin: 50% 100%; /* 设置旋转中心在底部 */
}
/* Rotate each arm to form a star */
.star-arm:nth-child(1) { transform: translate(-50%, -50%) rotate(0deg); }
.star-arm:nth-child(2) { transform: translate(-50%, -50%) rotate(72deg); }
.star-arm:nth-child(3) { transform: translate(-50%, -50%) rotate(144deg); }
.star-arm:nth-child(4) { transform: translate(-50%, -50%) rotate(216deg); }
.star-arm:nth-child(5) { transform: translate(-50%, -50%) rotate(288deg); }
js
handleGetRGB(event) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 获取点击位置在视口中的坐标
const clickX = event.clientX;
const clickY = event.clientY;
// 获取视口和文档尺寸
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// 设置 canvas 尺寸与文档一致(而非视口)
canvas.width = document.documentElement.scrollWidth;
canvas.height = document.documentElement.scrollHeight;
html2canvas(document.body).then(canvasSnapshot => {
// 创建一个临时 canvas 用于缩放计算
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// 设置 tempCanvas 尺寸为截图 canvas 的大小
tempCanvas.width = canvasSnapshot.width;
tempCanvas.height = canvasSnapshot.height;
// 绘制 html2canvas 的结果到 tempCanvas
tempCtx.drawImage(canvasSnapshot, 0, 0);
// 计算点击点在完整页面中的绝对位置
const fullPageWidth = canvas.width;
const fullPageHeight = canvas.height;
// 视口比例 -> 页面实际比例
const scaleX = tempCanvas.width / fullPageWidth;
const scaleY = tempCanvas.height / fullPageHeight;
const pixelX = Math.floor(clickX * scaleX);
const pixelY = Math.floor(clickY * scaleY);
// 获取像素数据
try {
const pixel = tempCtx.getImageData(pixelX, pixelY, 1, 1).data;
if(pixel[0] === 204 && pixel[1] === 204 && pixel[2] === 204 && pixel[3] === 255) {
console.log('点击区域外!');
}else if(pixel[0] === 255 && pixel[1] === 165 && pixel[2] === 0 && pixel[3] === 255) {
console.log('点击区域内!');
}
} catch (e) {
console.error('无法获取像素数据:', e);
}
});
}
总结
在生产环境中,建议通过SVG方案来实现特定区域的点击,最后一种方案其实有点儿“邪门”,不太推荐使用。(面试可以扯扯)
完整代码:
js
<template>
<div class="wrap">
<div @click="handleArea">
<svg width="200" height="200" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<polygon
points="50,5 61,35 95,35 67,57 78,90 50,70 22,90 33,57 5,35 39,35"
fill="orange" @click.stop="handleInArea" />
</svg>
<div>SVG</div>
</div>
<div @click="handleArea">
<div class="star" @click.stop="handleInArea"></div>
<div>clip-path</div>
</div>
<div @click="handleGetRGB">
<div class="start2">
<div class="star-container">
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
<div class="star-arm"></div>
</div>
</div>
<div style="margin-top: 20px">Js</div>
</div>
</div>
</template>
<script>
import html2canvas from "html2canvas";
export default {
name: 'AreaClick',
data() {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods: {
handleArea() {
console.log('点击区域外!');
},
handleInArea() {
console.log('点击区域内!');
},
handleGetRGB(event) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 获取点击位置在视口中的坐标
const clickX = event.clientX;
const clickY = event.clientY;
// 获取视口和文档尺寸
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// 设置 canvas 尺寸与文档一致(而非视口)
canvas.width = document.documentElement.scrollWidth;
canvas.height = document.documentElement.scrollHeight;
html2canvas(document.body).then(canvasSnapshot => {
// 创建一个临时 canvas 用于缩放计算
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
// 设置 tempCanvas 尺寸为截图 canvas 的大小
tempCanvas.width = canvasSnapshot.width;
tempCanvas.height = canvasSnapshot.height;
// 绘制 html2canvas 的结果到 tempCanvas
tempCtx.drawImage(canvasSnapshot, 0, 0);
// 计算点击点在完整页面中的绝对位置
const fullPageWidth = canvas.width;
const fullPageHeight = canvas.height;
// 视口比例 -> 页面实际比例
const scaleX = tempCanvas.width / fullPageWidth;
const scaleY = tempCanvas.height / fullPageHeight;
const pixelX = Math.floor(clickX * scaleX);
const pixelY = Math.floor(clickY * scaleY);
// 获取像素数据
try {
const pixel = tempCtx.getImageData(pixelX, pixelY, 1, 1).data;
if(pixel[0] === 204 && pixel[1] === 204 && pixel[2] === 204 && pixel[3] === 255) {
console.log('点击区域外!');
}else if(pixel[0] === 255 && pixel[1] === 165 && pixel[2] === 0 && pixel[3] === 255) {
console.log('点击区域内!');
}
} catch (e) {
console.error('无法获取像素数据:', e);
}
});
}
}
}
</script>
<style scoped>
.wrap {
width: 800px;
height: 240px;
background: #ccc;
display: flex;
justify-content: space-between;
text-align: center;
}
.star {
width: 200px;
height: 200px;
background-color: orange;
clip-path: polygon(
50% 5%,
61% 35%,
95% 35%,
67% 57%,
78% 90%,
50% 70%,
22% 90%,
33% 57%,
5% 35%,
39% 35%
);
}
.start2 {
display: flex;
justify-content: center;
align-items: center;
height: 240px;
margin-top: -60px;
}
.star-container {
position: relative;
width: 200px;
height: 200px;
}
.star-arm {
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
border-left: 30px solid transparent; /* 加宽角的宽度 */
border-right: 30px solid transparent;
border-bottom: 100px solid orange; /* 增加高度,控制五角星的纵向尺寸 */
transform-origin: 50% 100%; /* 设置旋转中心在底部 */
}
/* Rotate each arm to form a star */
.star-arm:nth-child(1) { transform: translate(-50%, -50%) rotate(0deg); }
.star-arm:nth-child(2) { transform: translate(-50%, -50%) rotate(72deg); }
.star-arm:nth-child(3) { transform: translate(-50%, -50%) rotate(144deg); }
.star-arm:nth-child(4) { transform: translate(-50%, -50%) rotate(216deg); }
.star-arm:nth-child(5) { transform: translate(-50%, -50%) rotate(288deg); }
</style>