首页 > 解决方案 > 如何通过 CSS 使用垂直居中的锚点为文本设置动画

问题描述

我有一个简单的 JS 脚本,它监听键盘输入并在随机位置显示每个键入的字母淡出并变小的简短动画。

'use strict'
const body = document.querySelector('body')
const ignoreKeys = [
    'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
    'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]

document.addEventListener('keydown', function(e) {
    if (!ignoreKeys.includes(e.key)) {
        // Values 400 & 200 keep the div completely inside the window
        const maxHeight = window.innerHeight - 400
        const maxWidth = window.innerWidth - 200

        const div = document.createElement('div')
        div.className = 'anim'
        div.textContent = e.key
        div.style.top = getRandomInt(0, maxHeight) + 'px'
        div.style.left = getRandomInt(0, maxWidth) + 'px'
        body.append(div)

        setTimeout(function() { div.remove() }, 3000)
    }
})

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min) + min)
}
.anim {
    position: fixed;
    text-align: center;
    animation-name: fade;
    animation-duration: 2s;
    animation-timing-function: ease-in-out;
    opacity: 0%;
}

@keyframes fade {
    0% { opacity: 100%; font-size: 300px;}
    100% { opacity: 0%; font-size: 100px;}
}

通过为属性设置动画font-size,每个字母都会变小。然而,由于它的“锚点”是 div 的顶部,可见效果是一个字母变小并略微向上移动。我希望每个字母都向垂直中心收缩div

我可以div轻松计算中心并将正确的top坐标添加到@keyframe属性中,但我不知道如何在 JS 中单独修改每个div. 这完全可以通过 CSS 实现吗?还是我应该用纯 JS 重写整个事情?

标签: javascriptcss

解决方案


您根本不需要调整 div 的最高值。由于 DIV 标签本身没有边框或其他任何显示 - 只是其中的字母 - 您可以调整边距、边框和/或填充以达到与增加 DIV 的顶部值相同的效果。由于这些中的每一个都可以在 css 转换中处理,因此您可以执行以下操作:

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min) + min)
}


document.addEventListener('keydown', function(e) {
    const body = document.querySelector('body')
    const ignoreKeys = [
        'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
        'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
    ]
    if (!ignoreKeys.includes(e.key)) {
        // Values 400 & 200 keep the div completely inside the window
        const maxHeight = window.innerHeight - 400
        const maxWidth = window.innerWidth - 200
        const div = document.createElement('div')
        div.className = 'anim'
        div.textContent = e.key;
        div.style.top = getRandomInt(0, maxHeight) + 'px'
        div.style.left = getRandomInt(0, maxWidth) + 'px'
        body.append(div)

        setTimeout(function() { div.remove() }, 3000)
    }
})
.anim {
  display:block;
  position: fixed;
  text-align: center;
  width:200px;
  animation-name: fade;
  animation-duration: 2s;
  animation-timing-function: ease-in-out;
  opacity: 0;
  margin:0px;
}

@keyframes fade {
    0% { opacity:1; font-size: 300px;}
    100% { opacity:0; font-size: 100px; margin-top:100px;}
}

DIV 的初始状态为 margin:0px。在关键帧 css 中添加 margin-top 设置,在过渡期间将其从 0 增加到 100。这样做的效果是向下推 DIV - 如上所述,由于 DIV 本身没有显示任何内容,因此用户不会看到它移动。请注意,我已将 DIV 的宽度固定为 200 像素,因此确保所有内容始终水平居中 - 否则 DIV 宽度基于字符的宽度,因此在过渡期间会发生变化,并且字符将向左移动作为中心线路变化。我已经移动了一些代码以使其更易于测试 - 但唯一实际的变化是 CSS 样式。另请注意,不透明度是从 0 到 1 的值,因此不应显示为百分比。

更新

看看下面的代码片段。我认为可能有随机字体大小和随机位置使用transform而不是animate.

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min) + min)
}

const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");

function zoomOUT(){
  let d = document.getElementById("test");
  d.classList.remove("zoomIN");
  d.classList.add("zoomOUT");
}

function zoomIN(){
  let d = document.getElementById("test");
  let dletter = document.getElementById("testletter");
  let t = getRandomInt(20, 60) * 10;
  let l = getRandomInt(20, 100) * 10;
  let fs = getRandomInt(10, 20) * 10;
  dletter.innerHTML = letters[getRandomInt(0, 61)];
  d.style.top = t + "px";
  d.style.left = l + "px";
  d.style.fontSize = fs + "%";
  d.classList.remove("zoomOUT");
  d.classList.add("zoomIN");
}
#test {
  position:absolute;
  padding: 50px;
  margin: 0 auto;
  text-align:center;
  vertical-align:middle;
}

.zoomIN {
  opacity:1;
  transform: scale(3);
  transition: transform 2s;
}

.zoomOUT {
  opacity:0.5;
  transform: scale(0.1);
  transition: transform 3s;
}
<button onclick="zoomOUT();" z-index=1>Play</button><button onclick="zoomIN();" z-index=1>Restart</button>

<div id="test" class="zoomIN" style="top:300px; left:300px;" z-index=0><div id="testletter" style="font-size:600%; width:100%; height:100%">A</div></div>

Transform 似乎将事物保持在同一个地方,因此根本不需要调整任何顶部/边距/边框/填充设置。事实上,唯一改变的是字体大小(使用scale(..))和不透明度。字体大小由代码决定。请注意,这要求字符位于 div 中的 div 中。这只是一个测试,但应该足以让您将内容转换为您的代码要求。


推荐阅读