目录
一、让文字适应纹理(过滤器)
<svg viewBox="0 0 600 330">
<defs>
<filter id="conform">
<feImage href="qiang.jpg" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"
result="ORIGIN_IMAGE"></feImage>
<feColorMatrix in="ORIGIN_IMAGE" type="saturate" values="0" result="GRAY_IMAGE"></feColorMatrix>
<feDisplacementMap in="SourceGraphic" in2="GRAY_IMAGE" scale="15" xChannelSelector="R" yChannelSelector="R"
result="TEXTURED_TEXT"></feDisplacementMap>
<feImage href="qiang.jpg" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none" result="BG">
</feImage>
<feColorMatrix in="TEXTURED_TEXT" result="OPACITY_TEXT" type="matrix" values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 .9 0"></feColorMatrix>
<feBlend in="BG" in2="PACITY_TEXT" mode="multiply"></feBlend>
</filter>
</defs>
<Image href="qiang.jpg" x="0" y="0" width="100%" height="100%" preserveAspectRatio="none"></Image>
<text x="50%" y="50%" font-size="10em" font-weight="bold" text-anchor="middle" alignment-baseline="middle"
fill="#000" filter="url(#conform)">
LOGO
</text>
</svg>
<filter id="conform">
:定义了一个名为conform
的过滤器。<feImage>
:加载背景图像qiang.jpg
,并将其结果命名为ORIGIN_IMAGE
。<feColorMatrix>
:将ORIGIN_IMAGE
转换为灰度图像,结果命名为GRAY_IMAGE
。<feDisplacementMap>
:使用GRAY_IMAGE
对源图形进行位移映射,结果命名为TEXTURED_TEXT
。<feImage>
:再次加载背景图像qiang.jpg
,结果命名为BG
。<feColorMatrix>
:给TEXTURED_TEXT
调整透明度,结果命名为OPACITY_TEXT
。<feBlend>
:将BG
和OPACITY_TEXT
进行乘法混合
二、交错排列的文字
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Luckiest+Guy&display=swap">
<style>
.overlap {
text-transform: uppercase;
font-family: "Luckiest Guy";
color: white;
font-size: 100px;
letter-spacing: -10px;
}
.overlap span {
text-shadow: 10px 0 10px #000;
position: relative;
}
</style>
</head>
<body>
<h1 class="overlap">Hello World</h1>
<script>
const overlap = document.querySelector(".overlap");
overlap.innerHTML = overlap.textContent
.split("")
.map(
(word, i, arr) => `<span style="z-index:${arr.length - i}">${word}</span>`
)
.join("");
</script>
</body>
三、标题动画
<style>
div {
margin-top: 100px;
display: flex;
flex-direction: column;
align-items: center;
}
.title {
font-size: 42px;
text-transform: uppercase;
letter-spacing: 5px;
transform: rotate(-10deg);
display: flex;
}
.title span {
opacity: 0;
transform: skew(-10deg); /* 倾斜350度 */
text-shadow: 1px 1px #533d4a, 2px 2px #533d4a, 3px 3px #533d4a,
4px 4px #533d4a, 5px 5px #533d4a;
/* 贝塞尔曲线做个动画轨迹 */
animation: move 1s var(--d) cubic-bezier(0.46, -0.15, 0.49, 1.48)
forwards;
}
@keyframes move {
from {
opacity: 0;
transform: skew(-10deg) translateY(300%);
}
to {
opacity: 1;
transform: skew(-10deg) translateY(0);
}
}
</style>
</head>
<body>
<div>
<p class="title" style="color: #e55643">这是会依次弹跳</p>
<p class="title" style="color: #2b9f5e">的标题文字</p>
</div>
<script>
const ps = document.querySelectorAll(".title");
ps.forEach((p) => {
const result = p.textContent
.split("")
.map((letter) => `<span>${letter}</span>`)
.join(``);
p.innerHTML = result;
});
// 依次弹跳效果
const spans = document.querySelectorAll(".title span");
for (let i = 0; i < spans.length; i++) {
spans[i].style.setProperty("--d", i * 0.1 + "s");
}
</script>
</body>
扩展:transform变形失效情况:
1、元素未设置 position或display
2、display: inline元素
3、3D 变换时缺乏适当的 perspective
4、父元素的overflow: hidden
5、transform 与 z-index 层级冲突
四、文字阴影
右上角有白边,左下方添加灰色阴影,字体是"Luckiest Guy"
text-shadow: 1px -1px #fff, -1px 1px #999, -10px 10px 5px #808080;
五、文字镂空效果
<style>
body {
--bg: url(./yejing.jpg) no-repeat center/cover;
background: var(--bg);
height: 100vh;
}
.modal {
background-color: rgba(0, 0, 0, 0.7);
width: 100%;
height: 100%;
}
.modal h1 {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 12vw;
text-stroke: 1px #fff;
-webkit-text-stroke: 1px #fff; /* 文字白色描边 */
background: var(--bg);
/* 设置背景只显示在文本区域内,使得文字背景呈现图片效果 */
background-clip: text;
-webkit-background-clip: text;
color: transparent;
}
</style>
</head>
<body>
<div class="modal"><h1>NightView</h1></div>
</body>
六、文字的交融展开(filter+调整字间距)
<style>
.container {
width: 100%;
height: 100vh;
background-color: black;
text-align: center;
line-height: 100vh;
/* 对比度滤镜:使容器内容的对比度增加到原来的30倍,造成“交融” */
filter: contrast(30);
}
.text {
font-size: 100px;
color: #fff;
animation: showup 3s forwards;
}
/* 字间距从-50px变化到10px */
@keyframes showup {
from {
letter-spacing: -50px;
filter: blur(10px); /* 模糊滤镜:使文本模糊 */
}
to {
letter-spacing: 10px;
filter: blur(2px);
}
}
</style>
</head>
<body>
<div class="container">
<span class="text">Web Developer</span>
</div>
</body>
七、光标跟随效果【仿AI回复】
<body>
<div class="text-container">
<div class="text"></div>
<!-- 一开始光标样式在盒子左上角 -->
<div class="cursor"></div>
</div>
<script>
const textContainer = document.querySelector(".text-container");
const textElem = document.querySelector(".text");
const cursor = document.querySelector(".cursor");
async function autoAppend() {
function delay(duration) {
return new Promise((resolve) => setTimeout(resolve, duration));
}
function transfer(text) {
return `<p>${text.replace(/\n/g, "<br>")}</p>`;
}
const content = `这段代码主要在模仿AI回复用户提问后,进行解答的场景,主要是为了看下光标跟随文字效果是如何实现的`;
for (let i = 0; i < content.length; i++) {
let text = content.slice(0, i);
let result = transfer(text);
textElem.innerHTML = result;
updateCursor();
await delay(100);
}
}
autoAppend();
function getLastTextNode(node) {
if (node.nodeType === Node.TEXT_NODE) {
return node;
}
const children = node.childNodes;
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
const result = getLastTextNode(child);
if (result) {
return result;
}
}
return null;
}
function updateCursor() {
// 1、追加一个文字到末尾
const lastTextNode = getLastTextNode(textElem);
const textNode = document.createTextNode("字");
if (lastTextNode) {
lastTextNode.parentNode.appendChild(textNode);
} else {
textElem.appendChild(textNode);
}
// 2、获取追加的文字位置
const range = document.createRange(); //框选范围对象
range.setStart(textNode, 0);
range.setEnd(textNode, 0);
const rect = range.getBoundingClientRect();
// 3、根据文字位置设置光标位置
const containerRect = textContainer.getBoundingClientRect();
const x = rect.x - containerRect.x;
const y = rect.y - containerRect.y;
cursor.style.transform = `translate(${x}px,${y}px)`;
// 4、删除追加的文字
textNode.parentNode.removeChild(textNode);
}
</script>
</body>
八、高度自动过渡+宽度适应内容
<style>
.btn {
width: 120px;
background-color: #007bff;
text-align: center;
border-radius: 5px;
cursor: pointer;
}
.detail {
width: 120px;
background-color: rosybrown;
overflow: hidden;
height: 0;
transition: height 0.3s ease;
}
.news-type{
/* 宽度适应内容,不写或者auto会占满容器 */
width: fit-content;
padding: 10px;
background-color: yellowgreen;
}
</style>
</head>
<body>
<div class="btn">hover me</div>
<div class="detail">
划过显示高度,高度自然过渡,css网格布局会有兼容性问题,其他css不够丝滑
</div>
<div class="news-type">热门话题</div>
<script>
const btn = document.querySelector(".btn");
const detail = document.querySelector(".detail");
btn.onmouseenter = function () {
detail.style.height = detail.scrollHeight + "px";
};
btn.onmouseleave = function () {
detail.style.height = "0";
};
</script>
</body>
九、文字智能适配背景(blend)
<style>
.banner {
position: relative;
width: 50%;
height: 50vh;
background: linear-gradient(-135deg, black 50%, white 50%);
}
.title {
position: absolute;
top: 50%;
left: 54%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 3em;
transition: 0.4s;
mix-blend-mode: difference;/* 文字颜色与背景色进行差值混合 */
}
.banner:hover .title {
transform: translate(-50%, -50%) translateX(-120px);
}
</style>
</head>
<body>
<div class="banner">
<h1 class="title">育人为渡 经久如一</h1>
</div>
</body>
十、不规则的文字环绕(shape-outside)
<style>
.avatar {
width: 100px;
height: 120px;
float: left;
margin-right: 1em;
object-fit: cover;
border-radius: 50%;
shape-outside: circle(50% at 50% 50%);
}
p {
line-height: 2;
}
</style>
</head>
<body>
<div>
<img src="./qiang.jpg" class="avatar" />
<p>若头像图片是直角图形,环绕文字会直线环绕;若头像图片是圆形这种不规则图片,继续直线环绕就不太搭配;此时需要不规则的文字环绕:这里使用shape-outside。</p>
</div>
</body>
十一、多行文本擦除效果
<style>
body {
background-color: #000;
color: #fff;
}
.container {
width: 80%;
margin: 1em auto;
line-height: 2;
text-indent: 2em;
position: relative;
}
.eraser {
position: absolute;
inset: 0;
}
.text {
--p: 0%;
background: linear-gradient(
to right,
#0000 var(--p),
#000 calc(var(--p) + 30px)
);
color: transparent;
animation: erase 5s linear forwards;
}
@property --p {
syntax: "<percentage>";
initial-value: 0%;
inherits: false;
}
@keyframes erase {
to {
--p: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<p>
当页面加载时,.text
元素中的文本会逐渐被擦除,就像用橡皮擦从左到右擦除一样。这是因为背景渐变从左到右移动,覆盖了文本,使其看起来像是被擦除。其中,@property --p:
定义了一个名为 --p 的 CSS 自定义属性,其语法为百分比 (<percentage>),初始值为 0%,且不继承,这样动画才能起效果,不然css变量无法让动画起效。
</p>
<p class="eraser">
<span class="text">
当页面加载时,.text
元素中的文本会逐渐被擦除,就像用橡皮擦从左到右擦除一样。这是因为背景渐变从左到右移动,覆盖了文本,使其看起来像是被擦除。其中,@property --p:
定义了一个名为 --p 的 CSS 自定义属性,其语法为百分比 (<percentage>),初始值为 0%,且不继承,这样动画才能起效果,不然css变量无法让动画起效。</span
>
</p>
</div>
</body>
十二、多行文本溢出处理
<style>
.singleText {
width: 200px;
height: 30px;
line-height: 30px;
margin-bottom: 20px;
border: 1px solid #f40;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.multilineText {
width: 200px;
height: 120px;
line-height: 30px;
border: 1px solid #f40;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 4;/* Firefox当前不支持-webkit-line-clamp */
overflow: hidden;
}
</style>
</head>
<body>
<div class="singleText">
单行文本内容过长,此时将溢出内容隐藏,并用省略号展示
</div>
<div class="multilineText">
多行文本内容过长,先创建一个多行文本的容器,再指定 Webkit-box 布局方向为
垂直,然后限制显示文本行数,最后超出部分隐藏
</div>
</body>