You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现旋转时钟指针的drop-shadow始终朝下

让时钟指针的投影始终朝下的解决方案

问题的核心在于:drop-shadow滤镜是跟随元素的变换(比如旋转)一起生效的,所以指针旋转时影子也会跟着转,没法单独控制影子的方向。要解决这个问题,我们需要把指针的旋转和影子的方向分离开,下面提供两种实用的实现方案:

方案一:使用独立的影子元素反向旋转

我们可以给每个指针添加一个对应的影子元素(复制指针图片),让指针正常旋转,同时让影子元素以相反的角度旋转,抵消指针的旋转效果,再通过偏移和模糊模拟朝下的投影。

步骤1:修改HTML结构

给每个指针增加一个影子元素:

<div class="hand-container">
  <img src="media/hour-hand.png" class="hourHand hand" />
  <img src="media/hour-hand.png" class="hourHand-shadow hand-shadow" />
</div>
<div class="hand-container">
  <img src="media/minute-hand.png" class="minuteHand hand" />
  <img src="media/minute-hand.png" class="minuteHand-shadow hand-shadow" />
</div>
<div class="hand-container">
  <img src="media/second-hand.png" class="secondHand hand" />
  <img src="media/second-hand.png" class="secondHand-shadow hand-shadow" />
</div>
<div class="hand-container small-second-container">
  <img src="media/small-second-hand.png" class="smallSecondHand hand" />
  <img src="media/small-second-hand.png" class="smallSecondHand-shadow hand-shadow" />
</div>

步骤2:编写基础CSS

设置容器、指针和影子的样式,让影子默认朝下:

.hand-container {
  position: absolute;
  top: 50%;
  left: 50%;
}
.small-second-container {
  left: calc(50% - 10vmin + 2px);
}
.hand {
  position: relative;
  z-index: 2;
  height: 30vmin;
  transform: translate(-50%, -50%);
}
.smallSecondHand {
  height: 12vmin;
}
.hand-shadow {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  height: inherit;
  opacity: 0.3;
  filter: blur(2px);
  /* 调整translate的Y值,让影子朝下偏移 */
  transform: translate(-50%, -38%) rotate(0deg);
}
.smallSecondHand-shadow {
  height: 12vmin;
}

步骤3:更新JavaScript逻辑

在时钟更新函数中,同时给指针和影子设置旋转角度(影子角度为指针角度的负值):

setInterval(clock, 10);
function clock() {
  let dt = new Date(), 
      hr = dt.getHours(), 
      min = dt.getMinutes(), 
      sec = dt.getSeconds(), 
      msec = dt.getMilliseconds(),
      hrDeg = 55 + (hr * 30) + (min * 30/60),
      minDeg = -51.7 + (min * 6) + (sec * 6/60),
      secDeg = (sec * 6) + (6 * msec/1000),
      // 获取指针和影子元素
      hrHand = document.querySelector('.hourHand'),
      minHand = document.querySelector('.minuteHand'),
      secHand = document.querySelector('.smallSecondHand'),
      hrShadow = document.querySelector('.hourHand-shadow'),
      minShadow = document.querySelector('.minuteHand-shadow'),
      secShadow = document.querySelector('.smallSecondHand-shadow');

  // 更新指针旋转
  hrHand.style.transform = `translate(-50%, -50%) rotate(${hrDeg}deg)`;
  minHand.style.transform = `translate(-50%, -50%) rotate(${minDeg}deg)`;
  secHand.style.transform = `translate(-50%, -50%) rotate(${secDeg}deg)`;

  // 更新影子旋转(反向抵消指针的旋转)
  hrShadow.style.transform = `translate(-50%, -38%) rotate(${-hrDeg}deg)`;
  minShadow.style.transform = `translate(-50%, -38%) rotate(${-minDeg}deg)`;
  secShadow.style.transform = `translate(-50%, -38%) rotate(${-secDeg}deg)`;
}

方案二:使用CSS伪元素 + 自定义属性(更简洁)

如果不想增加额外的HTML元素,可以用伪元素生成影子,结合CSS自定义属性来管理旋转角度,让伪元素自动反向旋转。

步骤1:修改HTML结构

用容器包裹每个指针:

<div class="hand-wrapper hour-wrapper">
  <img src="media/hour-hand.png" class="hourHand hand" />
</div>
<div class="hand-wrapper minute-wrapper">
  <img src="media/minute-hand.png" class="minuteHand hand" />
</div>
<div class="hand-wrapper second-wrapper">
  <img src="media/second-hand.png" class="secondHand hand" />
</div>
<div class="hand-wrapper small-second-wrapper">
  <img src="media/small-second-hand.png" class="smallSecondHand hand" />
</div>

步骤2:编写CSS

利用伪元素生成影子,通过自定义属性控制旋转:

.hand-wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  --rotate: 0deg;
  /* 容器旋转带动指针 */
  transform: translate(-50%, -50%) rotate(var(--rotate));
}
.small-second-wrapper {
  left: calc(50% - 10vmin + 2px);
}
.hand {
  position: relative;
  z-index: 2;
  height: 30vmin;
  transform: translate(-50%, -50%);
}
.smallSecondHand {
  height: 12vmin;
}
/* 伪元素作为影子 */
.hand-wrapper::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: -1;
  height: inherit;
  width: auto;
  opacity: 0.3;
  filter: blur(2px);
  /* 反向旋转抵消容器的旋转,保持影子朝下 */
  transform: translate(-50%, -38%) rotate(calc(-1 * var(--rotate)));
}
/* 给每个伪元素设置对应指针的图片 */
.hour-wrapper::after {
  background-image: url('media/hour-hand.png');
}
.minute-wrapper::after {
  background-image: url('media/minute-hand.png');
}
.second-wrapper::after {
  background-image: url('media/second-hand.png');
}
.small-second-wrapper::after {
  background-image: url('media/small-second-hand.png');
  height: 12vmin;
}

步骤3:更新JavaScript逻辑

只需要更新容器的自定义属性即可:

setInterval(clock, 10);
function clock() {
  let dt = new Date(), 
      hr = dt.getHours(), 
      min = dt.getMinutes(), 
      sec = dt.getSeconds(), 
      msec = dt.getMilliseconds(),
      hrDeg = 55 + (hr * 30) + (min * 30/60),
      minDeg = -51.7 + (min * 6) + (sec * 6/60),
      secDeg = (sec * 6) + (6 * msec/1000),
      // 获取容器元素
      hrWrapper = document.querySelector('.hour-wrapper'),
      minWrapper = document.querySelector('.minute-wrapper'),
      secWrapper = document.querySelector('.small-second-wrapper');

  // 更新容器的旋转角度
  hrWrapper.style.setProperty('--rotate', `${hrDeg}deg`);
  minWrapper.style.setProperty('--rotate', `${minDeg}deg`);
  secWrapper.style.setProperty('--rotate', `${secDeg}deg`);
}

原理说明

两种方案的核心都是分离指针旋转和影子方向:让指针正常旋转来指示时间,同时让影子元素(或伪元素)以相反的角度旋转,抵消指针的旋转效果,再通过Y轴偏移和模糊滤镜模拟出始终朝下的投影效果。这样就能避免drop-shadow跟随元素旋转的问题。

内容的提问来源于stack exchange,提问作者TheAndersMan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 04:59:05