Skip to content

【动画】类ChatGpt打字机的blink效果

Posted on:2024年8月7日 at 18:55

单行打字机效果

image 这里借助了 animation 的 steps 的特性实现,也就是逐帧动画。

从左向右和从上向下原理是一样的,以从左到右为例,假设我们有 26 个英文字符,我们已知 26 个英文字符组成的字符串长度,那么只需要设定一个动画,让它的宽度变化从 0-100%经历 26 帧即可,配合 overflow:hidden,steps 的每一帧即可展示一个字符。 划重点:这里是有技巧的,这样设置需要每次出来的字是等宽的,css 中,ch 单位表示”0”的宽度。如果字体恰巧又是等宽字体,即每个字符的宽度是一样的,此时 ch 就能变成每个英文字符宽度,那么 26ch 其实就是整个字符串的长度。 利用这个特性,配合 animation 的 steps,单行打字机代码如下:

<h1 > Pure CSS TyPing animation.</h1 > h1 {
  font-family: monospace;
  width: 26ch;
  white-space: nowrap;
  overflow: hidden;
  animation: typing 3s steps(26, end);
}
@keyframes typing {
  0 {
    width: 0;
  }
  100% {
    width: 26ch;
  }
}

上述打字机效果有下面两个局限: 1.限制了等宽字体,正常的页面一般没有用等宽字体,而是用常见的衬线与非衬线字体。 2.限制了单行文本,实际效果,GPT 的输出内容都是大段大段的,单行肯定无法满足需求,我们需要一种光标效果能适配多行文本的方式。

###用 background 实现多行光标效果 首先,需要将光标输出的内容动态生成。

 <template>
   <div ref="contentElement"> {{displayedText}} </div>
 </template>
<script setup>
 import {ref, onMounted, onUnmounted} from 'vue';
 const text = 'Lorem ipsum dolor sit amet consectetur elit ....'
 const displayedText = ref('')
 let index = ref(0)
 let timeoutId = null;
 const addNextCharacter = () => {
   if(index.value < text.length) {
    displayedText.value +=text[index.value];
    index.value++;
    timeoutId = setTimeOut(addNextCharacter,Math.random()*150+30);
   }
 };

 onMounted(() => {
   addNextCharacter();
 })

 onUnmounted(() => {
   //清除定时器,防止内存泄漏
   if(timeoutId) clearTimeout(timeoutId)
 })
 </script>

image

光标在行尾闪烁效果

在 display: inline 的 标签下,动画效果是以行为单位进行变换的。

针对这个特性,我们将我们的文本容器,改为 display: inline,然后给他设置一个特殊的背景,前 100% - 2px 宽度为一个颜色,最后 2px 为一个颜色。

完整代码如下:

:root {
  --pointerColor: #000;
}
p {
  display: inline;
  background: linear-gradient(
    90deg,
    transparent,
    transparent calc(100% - 2px),
    var(--pointerColor) calc(100% - 2px),
    var(--pointerColor)
  );
  animation: colorChange 0.8s linear infinite;
  padding-right: 4px;
}
@keyframes colorChange {
  0%,
  50% {
    --pointerColor: #000;
  }
  50%,
  100% {
    --pointerColor: transparent;
  }
}

除上述方法外,还可以用 animation 的 blink 效果来实现,具体代码如下:

animation: {
  blink: 'blink 1.2s infinite steps(1, start)'
  }
keyframes: {
blink: {
  '0%, 100%': { 'background-color': 'currentColor' },
  '50%': {
'background-color': 'transparent'
  },
 },
}

image