5950
0
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文字防剧透效果示例</title>
<style>
.spoiler {
cursor: pointer;
display: inline-block;
position: relative;
}
.spoiler-content {
padding: 10px;
clip-path: circle(0% at 50% 50%);
transition: clip-path 0.8s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.spoiler.revealed .spoiler-content {
clip-path: circle(100% at 50% 50%);
}
#particle-canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
transition: opacity 0.8s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.spoiler.revealed #particle-canvas {
opacity: 0;
}
.spoiler:not(.revealed) #particle-canvas {
opacity: 1;
}
</style>
</head>
<body>
<div class="spoiler" onclick="toggleSpoiler(this)">
<div class="spoiler-content">这是一段初始被隐藏的内容,点击粒子动画消失后就会显示</div>
<canvas id="particle-canvas"></canvas>
</div>
<script>
const canvas = document.getElementById('particle-canvas');
const ctx = canvas.getContext('2d');
const particles = [];
const particleCount = 800;
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
function getWeightedPosition(max) {
const margin = 0.1;
let pos;
do {
pos = Math.random() * max;
const distanceToEdge = Math.min(pos, max - pos);
const weight = distanceToEdge / (max * margin);
if (Math.random() < weight) {
break;
}
} while (true);
return pos;
}
class Particle {
constructor() {
this.x = getWeightedPosition(canvas.width);
this.y = getWeightedPosition(canvas.height);
this.size = Math.random() * 1 + 0.2;
this.speedX = (Math.random() - 0.5) * 2;
this.speedY = (Math.random() - 0.5) * 2;
this.opacity = 1;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
if (this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height) {
this.toRemove = true;
}
}
draw() {
if (!this.toRemove) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(0, 0, 0, ${this.opacity})`;
ctx.fill();
}
}
fadeOut(revealProgress) {
const distance = Math.sqrt((this.x - centerX) ** 2 + (this.y - centerY) ** 2);
const maxDistance = Math.sqrt(centerX ** 2 + centerY ** 2);
const fadeThreshold = revealProgress * maxDistance;
if (distance < fadeThreshold) {
this.opacity = Math.max(0, this.opacity - 0.05);
}
}
}
function initParticles() {
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle());
}
}
let revealProgress = 0;
let isRevealing = false;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isRevealing) {
revealProgress = Math.min(1, revealProgress + 0.02);
}
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.update();
if (isRevealing) {
particle.fadeOut(revealProgress);
}
if (particle.toRemove) {
particles.splice(i, 1);
} else {
particle.draw();
}
}
// 检查粒子数量,不足则生成新粒子
while (particles.length < particleCount) {
particles.push(new Particle());
}
requestAnimationFrame(animate);
}
function toggleSpoiler(element) {
element.classList.toggle('revealed');
if (element.classList.contains('revealed')) {
isRevealing = true;
} else {
isRevealing = false;
revealProgress = 0;
particles.forEach(particle => {
particle.opacity = 1;
particle.toRemove = false;
});
}
}
initParticles();
animate();
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>遮挡图片效果示例</title>
<style>
.spoiler {
cursor: pointer;
display: inline-block;
position: relative;
}
.spoiler-content {
padding: 10px;
position: relative;
}
.spoiler-image {
/* 加深毛玻璃效果,将模糊半径从 10px 增加到 20px */
filter: blur(20px);
transition: filter 1.2s cubic-bezier(0.25, 0.1, 0.25, 1);
}
.spoiler.revealed .spoiler-image {
filter: blur(0);
}
.particle-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: auto;
transition: opacity 1.2s cubic-bezier(0.25, 0.1, 0.25, 1);
opacity: 1;
}
.spoiler.revealed .particle-mask {
opacity: 0;
}
</style>
</head>
<body>
<div class="spoiler" id="spoiler-container">
<div class="spoiler-content">
<img class="spoiler-image" id="spoiler-img" src="https://www.meidecloud.com/xc.jpg" alt="图片" />
<div class="particle-mask">
<canvas id="particle-canvas"></canvas>
</div>
</div>
</div>
<script>
const canvas = document.getElementById('particle-canvas');
const img = document.getElementById('spoiler-img');
const ctx = canvas.getContext('2d');
const particles = [];
let bubble;
const density = 0.01; // 粒子密度,可根据需要调整
let particleCount = 0;
const spoilerContainer = document.getElementById('spoiler-container');
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
// 根据图片面积动态计算粒子数量
particleCount = Math.floor(canvas.width * canvas.height * density);
function getWeightedPosition(max) {
const margin = 0.1;
let pos;
do {
pos = Math.random() * max;
const distanceToEdge = Math.min(pos, max - pos);
const weight = distanceToEdge / (max * margin);
if (Math.random() < weight) {
break;
}
} while (true);
return pos;
}
class Particle {
constructor() {
this.x = getWeightedPosition(canvas.width);
this.y = getWeightedPosition(canvas.height);
this.size = Math.random() * 1 + 0.2;
this.speedX = (Math.random() - 0.5) * 2;
this.speedY = (Math.random() - 0.5) * 2;
this.opacity = 1;
}
update() {
this.x += this.speedX;
this.y += this.speedY;
if (this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height) {
this.toRemove = true;
}
}
draw() {
if (!this.toRemove) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`;
ctx.fill();
}
}
fadeOut(revealProgress) {
const distance = Math.sqrt((this.x - centerX) ** 2 + (this.y - centerY) ** 2);
const maxDistance = Math.sqrt(centerX ** 2 + centerY ** 2);
const fadeThreshold = revealProgress * maxDistance;
if (distance < fadeThreshold) {
this.opacity = Math.max(0, this.opacity - 0.05);
}
}
}
class Bubble {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 0;
this.maxRadius = Math.max(canvas.width, canvas.height);
this.opacity = 0.8; // 初始透明度
this.speed = 2;
}
update() {
this.radius += this.speed;
this.opacity = Math.max(0, this.opacity - 0.01); // 逐渐降低透明度
if (this.radius >= this.maxRadius) {
this.toRemove = true;
}
}
draw() {
if (!this.toRemove) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`; // 白色透明填充
ctx.fill();
ctx.strokeStyle = `rgba(255, 255, 255, ${this.opacity})`;
ctx.lineWidth = 2;
ctx.stroke();
}
}
}
function createBubble(x, y) {
bubble = new Bubble(x, y);
}
function initParticles() {
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle());
}
}
let revealProgress = 0;
let isRevealing = false;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (isRevealing) {
revealProgress = Math.min(1, revealProgress + 0.02);
}
for (let i = particles.length - 1; i >= 0; i--) {
const particle = particles[i];
particle.update();
if (isRevealing) {
particle.fadeOut(revealProgress);
}
if (particle.toRemove) {
particles.splice(i, 1);
} else {
particle.draw();
}
}
if (bubble) {
bubble.update();
if (bubble.toRemove) {
bubble = null;
} else {
bubble.draw();
// 根据气泡半径控制图片显示区域
ctx.globalCompositeOperation = 'destination-in';
ctx.beginPath();
ctx.arc(bubble.x, bubble.y, bubble.radius, 0, Math.PI * 2);
ctx.fill();
ctx.globalCompositeOperation = 'source-over';
}
}
// 绘制气泡遮罩
if (isRevealing) {
ctx.globalCompositeOperation = 'destination-in';
ctx.fillStyle = `rgba(255, 255, 255, ${revealProgress})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'source-over';
}
// 检查粒子数量,不足则生成新粒子
while (particles.length < particleCount) {
particles.push(new Particle());
}
requestAnimationFrame(animate);
}
function toggleSpoiler() {
spoilerContainer.classList.toggle('revealed');
if (spoilerContainer.classList.contains('revealed')) {
isRevealing = true;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
createBubble(centerX, centerY);
} else {
isRevealing = false;
revealProgress = 0;
particles.forEach(particle => {
particle.opacity = 1;
particle.toRemove = false;
});
bubble = null;
}
}
spoilerContainer.addEventListener('click', toggleSpoiler);
initParticles();
animate();
};
</script>
</body>
</html>
本人发表的文字只是用来测试键盘是否可用,不代表本人立场