Angular中如何实现textarea逐字逐词逐行动画效果及钢笔光标模拟
问题修复实现方案
原方案失效原因
参考的demo是针对普通DOM元素实现的动画,核心逻辑是将文本拆分为独立<span>标签,通过控制每个标签的透明度、位移实现动效。但<textarea>属于可替换元素,内部仅支持纯文本,无法解析嵌套HTML标签,因此直接复用原有CSS类会完全失效。
适配textarea的动画实现
我们改用动态拼接textarea.value属性的方式实现三类动画效果,具体实现如下:
基础结构
<div class="textarea-wrapper"> <textarea id="animTextarea" rows="10" cols="50"></textarea> <!-- 钢笔光标元素 --> <div class="pen-cursor"></div> </div>
样式代码
.textarea-wrapper { position: relative; display: inline-block; } #animTextarea { font-size: 16px; line-height: 1.8; padding: 8px; caret-color: transparent; /* 隐藏原生光标 */ outline: none; border: 1px solid #eee; } .pen-cursor { position: absolute; width: 18px; height: 26px; left: 10px; top: 10px; pointer-events: none; transition: all 0.08s linear; /* 钢笔图标,可替换为自定义样式 */ background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23222"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>') no-repeat center/contain; }
功能逻辑代码
const textarea = document.getElementById('animTextarea'); const cursor = document.querySelector('.pen-cursor'); const lineHeight = parseInt(getComputedStyle(textarea).lineHeight); const paddingLeft = parseInt(getComputedStyle(textarea).paddingLeft); const paddingTop = parseInt(getComputedStyle(textarea).paddingTop); // 待展示的文本内容 const fullContent = `第一行演示文本 第二行测试逐词展示效果 第三行可以写更长的内容验证自动滚动 第四行结束动画`; // 动画配置 const animConfig = { // 动画模式:char逐字 / word逐词 / line逐行 mode: 'char', // 每个单元动画间隔,单位ms interval: 80, // 是否展示钢笔光标 showCursor: true }; // 光标位置同步方法 function updateCursorPosition() { const lineNum = textarea.value.substr(0, textarea.selectionEnd).split('\n').length; const colNum = textarea.value.substr(0, textarea.selectionEnd).split('\n').pop().length; // 计算坐标,1px是字体宽度偏移,可根据实际字体调整 cursor.style.left = `${paddingLeft + colNum * 8 + 1}px`; cursor.style.top = `${paddingTop + (lineNum - 1) * lineHeight}px`; textarea.scrollTop = textarea.scrollHeight; } // 动画启动方法 async function runTextAnimation() { textarea.value = ''; let contentUnits = []; // 按配置拆分文本单元 if (animConfig.mode === 'line') { contentUnits = fullContent.split('\n').map(line => line + '\n'); } else if (animConfig.mode === 'word') { contentUnits = fullContent.split(/(\s|\n)/); } else { contentUnits = fullContent.split(''); } for (const unit of contentUnits) { textarea.value += unit; textarea.selectionEnd = textarea.value.length; if (animConfig.showCursor) updateCursorPosition(); await new Promise(resolve => setTimeout(resolve, animConfig.interval)); } } // 启动动画 runTextAnimation();
扩展说明
如果需要沿用原demo的animejs缓动效果,可将文本拼接逻辑封装到anime实例的update回调中,通过动画进度控制当前展示的文本长度,即可匹配原demo的动画节奏。
内容的提问来源于stack exchange,提问作者Kris Swat
相关产品推荐
相关产品推荐

