<svg>
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 2" />
</filter>
</svg>

直截了当,没错是用 svg 来实现,其实 css 的样式与 svg 还是蛮接近的(一模一样)

效果

使用

未使用(使用 blur)

鸿蒙开机效果

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Title</title>
</head>

<style>
body {
margin: 0;
height: 100vh;
display: block;
}

.ct-circle-content {
--circle-width: 100px;
--circle-height: 100px;
display: flex;
flex-direction: column;
gap: 16px;
margin: 100px auto;
width: 30%;
height: 40%;
}

.ct-circle-container {
display: flex;
align-items: center;
justify-content: center;
background: black;
}

.circle-wrap {
width: var(--circle-width, 100px);
height: calc(var(--circle-height) / 2);
padding: 40px;
overflow: hidden;
}

.circle {
height: var(--circle-height);
border: 10px solid #fff;
border-radius: 50%;
box-shadow: 0 0 10px 0 #fff, inset 0 0 10px 0 #fff;
box-sizing: border-box;
}

.circle-wrap:first-of-type {
padding-bottom: 0;
}

.circle-wrap:first-of-type .circle {
transform: translateY(calc(var(--circle-height) / 2));
animation: 1.2s ease forwards;
animation-name: move;
}

.circle-wrap:last-of-type {
transform: rotate(180deg);
padding-bottom: 0;
}

.circle-wrap:last-of-type .circle {
transform: translateY(calc(var(--circle-height) / 2));
/* animation-fill-mode:forwards;指定动画在结束时如何处理,forwards 为保持动画在结束后保持最终状态 */
animation: 1.2s ease forwards;
animation-name: move;
filter: url(#blur);
}

svg {
width: 0;
height: 0;
}

@keyframes move {
to {
transform: translateY(0px);
}
}
</style>

<body>
<div class="ct-circle-content">
<div class="ct-circle-actions">
<button class="ct-circle-start">start</button>
<button class="ct-circle-pause">pause</button>
</div>

<div class="ct-circle-container">
<div class="ct-circle-wrap">
<div class="circle-wrap">
<div class="circle"></div>
</div>

<div class="circle-wrap">
<div class="circle"></div>
</div>
</div>

<svg class="ct-circle-blur">
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="0 2" />
</filter>
</svg>
</div>
</div>
</body>

<script>
// 清除 svg 模糊
const svgBlur = document.querySelector('#blur feGaussianBlur');
const startButton = document.querySelector('.ct-circle-start');
const pauseButton = document.querySelector('.ct-circle-pause');
const firstCircle = document.querySelector('.circle-wrap:first-of-type .circle');
const lastCircle = document.querySelector('.circle-wrap:last-of-type .circle');
const animationRunStatusMap = {
running: 'running',
paused: 'paused',
start: 'start',
end: 'end'
}
let animationRunStatus;
let blurValue = 2;

const getCurrentElBlur = () => {
return svgBlur.getAttribute('stdDeviation').split(' ')[1];
};

const setCircleRunStatus = (status) => {
document.querySelector('.circle-wrap:first-of-type .circle').style.animationPlayState =
status;
document.querySelector('.circle-wrap:last-of-type .circle').style.animationPlayState = status;
};

const clearBlur = () => {
if (animationRunStatus === animationRunStatusMap.paused) {
return;
}
if (animationRunStatus === animationRunStatusMap.end) {
blurValue = 0;
svgBlur.setAttribute('stdDeviation', `0 0`);
return;
}

const value = getCurrentElBlur() - 0.01;
blurValue = value;
if (value > 0 && animationRunStatus === animationRunStatusMap.running) {
svgBlur.setAttribute('stdDeviation', `0 ${value}`);
} else {
return;
}
requestAnimationFrame(clearBlur);
};

startButton.addEventListener('click', () => {
if (animationRunStatus === animationRunStatusMap.running) {
return;
}

if (blurValue === 0) {
firstCircle.style.animationName = 'none';
lastCircle.style.animationName = 'none';
svgBlur.setAttribute('stdDeviation', `0 2`);
lastCircle.offsetWidth;
firstCircle.style.animationName = 'move';
lastCircle.style.animationName = 'move';
} else {
setCircleRunStatus('running');
clearBlur();
}
});

pauseButton.addEventListener('click', () => {
if (animationRunStatus !== animationRunStatusMap.running) {
return;
}

animationRunStatus = animationRunStatusMap.paused;

setCircleRunStatus('paused');
});

lastCircle.addEventListener('animationstart', (event) => {
console.log('animationstart', event);
animationRunStatus = animationRunStatusMap.running;
clearBlur();
});

lastCircle.addEventListener('animationend', (event) => {
console.log('animationend', animationRunStatus, event);
animationRunStatus = animationRunStatusMap.end;
blurValue = 0;
clearBlur();
});
</script>
</html>