一个按钮,每点击一次在大小可随时变化的按钮表面生成一个实心圆形,对每个圆形配置的时间 T T T(单位:毫秒)内有如下过程:
DOCTYPE html>
<html>
<style>
body {
text-align: center;
padding: 5rem;
}
.content {
display: inline-block;
position: relative;
overflow: hidden;
margin: 20px;
box-sizing: border-box;
transition: all ease-in-out .2s;
}
.content:hover {
background-color: #f001;
}
.circle {
width: 0;
height: 0;
border-radius: 100%;
border-style: solid;
z-index: 1;
position: absolute;
border-color: #f003;
box-sizing: border-box;
opacity: 0;
}
.text {
display: table-cell;
width: 300px;
height: 100px;
text-align: center;
vertical-align: middle;
}
@keyframes ani {
0% {
border-width: 0px;
opacity: .8;
transform: translate(0, 0);
}
100% {
opacity: 0;
}
}
style>
<body>
<div class="container">
<span class="content" id="content">
<div class="text">
button
div>
span>
div>
body>
<script>
const TIME = 500;
document.getElementById('content').addEventListener(
'click',
(e) => {
var e = event || window.event;
var target = document.getElementById('content');
var rect = target.getBoundingClientRect();
var circle = document.createElement('div');
circle.className = 'circle'
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var w = Math.max(target.offsetWidth - x, x);
var h = Math.max(target.offsetHeight - y, y);
var d = Math.sqrt(w * w + h * h);
circle.style = `border-width:${d}px;left:${x}px;top:${y}px;animation:ani ${TIME}ms ease-out 0s 1;transform:translate(-${d}px,-${d}px)`;
target.appendChild(circle);
setTimeout(() => { circle.parentElement.removeChild(circle) }, TIME); // 动画结束就去掉元素,要不然元素越来越多
}
)
script>
html>
DOCTYPE html>
<html>
<style>
body {
text-align: center;
padding: 5rem;
}
.content {
display: inline-block;
position: relative;
overflow: hidden;
margin: 20px;
box-sizing: border-box;
transition: all ease-in-out .2s;
}
.content:hover {
background-color: #f001;
}
#canvas {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.text {
display: table-cell;
width: 30vw;
height: 10vw;
text-align: center;
vertical-align: middle;
}
@keyframes ani {
0% {
border-width: 0px;
opacity: .8;
transform: translate(0, 0);
}
100% {
opacity: 0;
}
}
style>
<body>
<div class="container">
<span class="content" id="content">
<canvas id="canvas">canvas>
<div class="text">
button
div>
span>
div>
body>
<script>
const TICK = 1000 / 30;
const TIME = 500;
const CONTENT = document.getElementById('content');
const CANVAS = document.getElementById("canvas");
const DEBUG = document.getElementById("debug");
const ROUND_RAD = 2 * Math.PI;
const ZOOM = 3; // 因为动画很快所以图形边缘是否锐利没那么重要,故适度缩小绘图
var circles = [];
CONTENT.addEventListener(
'click',
(e) => {
let rect = CONTENT.getBoundingClientRect();
let w = CONTENT.offsetWidth;
let h = CONTENT.offsetHeight;
let x = (e.clientX - rect.left) / w;
let y = (e.clientY - rect.top) / h;
let dx = Math.max(x, 1 - x);
let dy = Math.max(y, 1 - y);
circles.push({
x: x,
y: y,
rMax: Math.sqrt(dx * dx + dy * dy) / Math.sqrt(2),
age: 0,
})
}
)
function callback () {
let rect = CONTENT.getBoundingClientRect(); // 元素大小是可变的,考虑到css动画,元素大小和窗口大小也没必然联系
let width = CONTENT.offsetWidth / ZOOM;
let height = CONTENT.offsetHeight / ZOOM;
let scale = Math.sqrt(width * width + height * height);
CANVAS.width = width;
CANVAS.height = height;
var ctx = CANVAS.getContext("2d");
while (circles[0] && circles[0].deleted) {
circles.shift();
}
for (let i = 0, len = circles.length; i < len; i++) {
const circle = circles[i];
var age = circle.age * TICK / TIME;
var opacity = (1 - age) * .3;
if (opacity <= 0) {
circle.deleted = true;
continue;
}
circle.age += 1
ctx.beginPath();
ctx.fillStyle = `rgba(255,0,0,${opacity})`;
ctx.arc(circle.x * width, circle.y * height, age * circle.rMax * scale, 0, ROUND_RAD);
ctx.fill();
ctx.closePath();
}
}
setInterval(callback, TICK);
script>
html>
DOCTYPE html>
<html>
<style>
body {
text-align: center;
padding: 5rem;
}
.content {
display: inline-block;
position: relative;
overflow: hidden;
margin: 20px;
box-sizing: border-box;
transition: all ease-in-out .2s;
}
.content:hover {
background-color: #f001;
}
#canvas {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.text {
display: table-cell;
width: 30vw;
height: 10vw;
text-align: center;
vertical-align: middle;
}
@keyframes ani {
0% {
border-width: 0px;
opacity: .8;
transform: translate(0, 0);
}
100% {
opacity: 0;
}
}
style>
<body>
<div class="container">
<span class="content" id="content">
<canvas id="canvas">canvas>
<div class="text">
button
div>
span>
div>
body>
<script>
const TIME = 500;
const CONTENT = document.getElementById('content');
const CANVAS = document.getElementById("canvas");
const DEBUG = document.getElementById("debug");
const ROUND_RAD = 2 * Math.PI;
const ZOOM = 3;
var circles = [];
var lastTime = new Date().getTime();
CONTENT.addEventListener(
'click',
(e) => {
let rect = CONTENT.getBoundingClientRect();
let w = CONTENT.offsetWidth;
let h = CONTENT.offsetHeight;
let x = (e.clientX - rect.left) / w;
let y = (e.clientY - rect.top) / h;
let dx = Math.max(x, 1 - x);
let dy = Math.max(y, 1 - y);
circles.push({
x: x,
y: y,
rMax: Math.sqrt(dx * dx + dy * dy) / Math.sqrt(2),
age: 0,
})
}
)
function callback () {
let now = new Date().getTime();
let tick = now - lastTime;
lastTime = now;
let rect = CONTENT.getBoundingClientRect();
let width = CONTENT.offsetWidth / ZOOM;
let height = CONTENT.offsetHeight / ZOOM;
let scale = Math.sqrt(width * width + height * height);
CANVAS.width = width;
CANVAS.height = height;
var ctx = CANVAS.getContext("2d");
while (circles[0] && circles[0].deleted) {
circles.shift();
}
for (let i = 0, len = circles.length; i < len; i++) {
const circle = circles[i];
var age = circle.age * tick / TIME;
var opacity = (1 - age) * .3;
if (opacity <= 0) {
circle.deleted = true;
continue;
}
circle.age += 1
ctx.beginPath();
ctx.fillStyle = `rgba(255,0,0,${opacity})`;
ctx.arc(circle.x * width, circle.y * height, age * circle.rMax * scale, 0, ROUND_RAD);
ctx.fill();
ctx.closePath();
}
requestAnimationFrame(callback);
}
requestAnimationFrame(callback);
script>
html>