商城網(wǎng)站建設(shè)開發(fā)公司開魯seo服務(wù)
目錄
一、弧形邊框選項(xiàng)卡
二、零寬字符
三、目錄滾動(dòng)時(shí)自動(dòng)高亮
四、高亮關(guān)鍵字
五、文字描邊
六、按鈕邊框的旋轉(zhuǎn)動(dòng)畫
七、視頻文字特效
八、立體文字特效+讓文字立起來
九、文字連續(xù)光影特效
十、重復(fù)漸變的邊框
十一、磨砂玻璃效果
十二、FLIP動(dòng)畫
一、弧形邊框選項(xiàng)卡
<style>.tab {width: 150px;height: 40px;color: #fff;text-align: center;line-height: 40px;margin: 0 auto;background: #ed6a5e;border-radius: 10px 10px 0 0;position: relative;transform: perspective(30px) rotateX(13deg);transform-origin: center bottom; /* 以tab最下方的線為中心進(jìn)行旋轉(zhuǎn) */}.tab::before,.tab::after {content: "";position: absolute;width: 10px;height: 10px;bottom: 0;background: #000;}.tab::before {left: -10px;background: radial-gradient(circle at 0 0,transparent 10px,#ed6a5e 10px); /* 左邊以左上角為圓點(diǎn)進(jìn)行徑向漸變 */}.tab::after {right: -10px;background: radial-gradient(circle at 100% 0,transparent 10px,#ed6a5e 10px); /* 右邊以右上角為圓點(diǎn)進(jìn)行徑向漸變 */}</style>
二、零寬字符
<script>// 判斷字符串中是否包含零寬字符function containsZeroWidthCharacter(str) {// 正則表達(dá)式匹配零寬字符const zeroWidthRegex = /[\u200B\u200C\u200D\uFEFF]/g;return zeroWidthRegex.test(str);}console.log(containsZeroWidthCharacter("Hello\u200B World")); // trueconsole.log(containsZeroWidthCharacter("Hello World")); // false</script>
常見的零寬字符包括:
1、零寬空格【Unicode:U+200B】
這是一個(gè)沒有寬度的空格,可以在兩個(gè)字符之間插入,但不會(huì)在視覺上產(chǎn)生間隔。
2、零寬非連接符【Unicode:U+200C】
用于阻止字符連接。在阿拉伯語或印地語中,用來控制哪些字符應(yīng)該連接,哪些不應(yīng)連接。
3、零寬連接符【Unicode:U+200D】
用于強(qiáng)制某些字符連接在一起,這通常在一些復(fù)合字符或表情符號(hào)中起作用。與2相反
4、零寬非換行空格【Unicode:U+FEFF】
用于文件中的字節(jié)順序標(biāo)記,但也可以用作零寬空格的一種形式。作用是防止換行。
盡管零寬字符對用戶不可見,但它們會(huì)占用存儲(chǔ)空間,通常用于文本隱寫、防止鏈接自動(dòng)化處理、格式化和排版。比如文本處理時(shí)加上零寬字符,可以防止文本被盜竊,解碼后是自己的名稱。
三、目錄滾動(dòng)時(shí)自動(dòng)高亮
<style>body {display: flex;margin: 0;}/* 左側(cè)目錄 */.sidebar {width: 250px;background-color: #333;color: white;padding: 20px;height: 100vh;position: fixed;top: 0;left: 0;overflow-y: auto;}.sidebar a {color: white;text-decoration: none;display: block;padding: 10px;margin: 5px 0;}.sidebar a:hover {background-color: #575757;}.highlight {background-color: #ffcc00; /* 高亮顏色 */}/* 右側(cè)內(nèi)容 */.content {margin-left: 280px; /* 留出左側(cè)目錄欄空間 */padding: 20px;width: calc(100% - 260px);}h1 {color: #333;}.section {margin-bottom: 30px;height: 750px;border: 1px solid yellowgreen;}.section h2 {color: #444;}</style><body><div class="sidebar toc"><h2>目錄</h2><a href="#section1">部分 1</a><a href="#section2">部分 2</a><a href="#section3">部分 3</a><a href="#section4">部分 4</a></div><div class="content"><div class="section" id="section1"><h2>部分 1</h2><p>第一部分內(nèi)容。</p></div><div class="section" id="section2"><h2>部分 2</h2><p>第二部分內(nèi)容。</p></div><div class="section" id="section3"><h2>部分 3</h2><p>第三部分內(nèi)容。</p></div><div class="section" id="section4"><h2>部分 4</h2><p>第四部分內(nèi)容。</p></div></div><script>function highlight(id) {document.querySelectorAll("a.highlight").forEach((a) => a.classList.remove("highlight"));if (id instanceof HTMLElement) {id.classList.add("highlight");return;}if (id.startsWith("#")) {id = id.substring(1);}document.querySelector(`a[href="#${id}"]`).classList.add("highlight");}const links = document.querySelectorAll('.toc a[href^="#"]');const titles = [];for (const link of links) {link.addEventListener("click", () => {highlight(link.getAttribute("href").substring(1));});const url = new URL(link.href);const dom = document.querySelector(url.hash);if (dom) {titles.push(dom);}}function debounce(fn, delay = 100) {let timer = null;return function (...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};}const scrollHandler = debounce(() => {const rects = titles.map((title) => title.getBoundingClientRect());const range = 300;for (let i = 0; i < titles.length; i++) {const title = titles[i];const rect = rects[i];// 標(biāo)題區(qū)域在指定范圍內(nèi)就高亮if (rect.top >= 0 && rect.top <= range) {highlight(title.id);break;}// 當(dāng)前內(nèi)容標(biāo)題在展示視口之上,并且下一個(gè)標(biāo)題在展示視口之下,此時(shí)高亮此標(biāo)題if (rect.top < 0 &&rects[i + 1] &&rects[i + 1].top > document.documentElement.clientHeight) {highlight(title.id);break;}}}, 100);window.addEventListener("scroll", scrollHandler);</script></body>
四、高亮關(guān)鍵字
<style>.highlight {color: red;font-weight: bold;}
</style><body><div><input type="text" class="txtKeyword" /><ul></ul></div><script>const ul = document.querySelector("ul");const txtKeyword = document.querySelector(".txtKeyword");function setHTML(lists) {ul.innerHTML = lists.map((l) => {let cname = l.cname;// 如果輸入框有內(nèi)容,則進(jìn)行高亮匹配if (txtKeyword.value) {const reg = new RegExp(txtKeyword.value, "ig");cname = cname.replace(reg, function (key) {return `<span class="highlight">${key}</span>`;});}return `<li><span>${cname}</span></li>`;}).join(""); // 使用 join 合并生成的 HTML 字符串}const NameLists = [{ cname: "前端" },{ cname: "后端" },{ cname: "測試員" },{ cname: "運(yùn)維師" },];// 篩選包含關(guān)鍵字的元素function filterList() {const keyword = txtKeyword.value.trim();if (keyword) {const filtered = NameLists.filter((item) =>item.cname.match(new RegExp(keyword, "i")));setHTML(filtered);} else {setHTML(NameLists);}}setHTML(NameLists);// 給輸入框添加監(jiān)聽事件,以便動(dòng)態(tài)更新txtKeyword.addEventListener("input", filterList);</script></body>
五、文字描邊
第一種text-shadow:給8個(gè)方向即可,但是連接處有瑕疵(見紅色邊緣部分)
@mixin text-stroke($color: #fff, $width: 1px) {text-shadow: 0 -#{$width} #{$color}, #{$width} 0 #{$color},0 #{$width} #{$color}, -#{$width} 0 #{$color}, #{$width} #{$width} #{$color},-#{$width} -#{$width} #{$color}, #{$width} -#{$width} #{$color},-#{$width} #{$width} #{$color};
}
p {font-size: 50px;font-weight: bold;@include text-stroke(red, 2px);// color: transparent; 不支持文字透明
}
p {font-size: 50px;font-weight: bold;text-shadow: 0 -2px gold, 2px 0 gold, 0 2px gold, -2px 0 gold,/* 上、右、下、左 */ 2px 2px gold, -2px -2px gold, 2px -2px gold,-2px 2px gold; /* 四個(gè)對角線 */
}
第二種-webkit-text-stroke不僅邊緣平滑,并且支持透明
p {font-size: 50px;font-weight: bold;-webkit-text-stroke: 2px red;color: transparent; //支持文字透明position: relative;
}
// -webkit-text-stroke是居中描邊,原來字體變小了,可以在"畫一層"蓋上去
p::after {content: attr(data-text);position: absolute;left: 0;top: 0;-webkit-text-stroke: 0;
}
六、按鈕邊框的旋轉(zhuǎn)動(dòng)畫
原理:在按鈕層級(jí)下加一個(gè)矩形,圍繞按鈕中心進(jìn)行360度旋轉(zhuǎn),多余矩形隱藏
<style>button {width: 100px;height: 50px;color: white;outline: none;z-index: 1;border-radius: 10px;cursor: pointer;background: black;/* outline: 4px solid gold; */position: relative;overflow: hidden;}button::before {content: "";position: absolute;width: 200%;height: 200%;background: blue;z-index: -2;left: 50%;top: 50%;transform-origin: left top;/* 圓點(diǎn)在左上角 */animation: rotation 2s linear infinite;}button::after {content: "";position: absolute;--g: 4px;width: calc(100% - var(--g) * 2);height: calc(100% - var(--g) * 2);background: black;left: var(--g);top: var(--g);border-radius: inherit;z-index: -1;}@keyframes rotation {to {transform: rotate(360deg);}}</style>
七、視頻文字特效
<style>.txt{position:absolute;inset: 0;background: #fff;display: flex;justify-content: center;align-items: center;mix-blend-mode: screen; /* 增強(qiáng)亮度,使圖像或元素顯得更加明亮 */}</style>
<body><div class="container"><video src="./fire.mp4" autoplay muted></video><div class="txt">大前端</div></div>
</body>
八、立體文字特效+讓文字立起來
放大看是疊加出來的,真正要做得建模
<style>body {background-color: brown;color: #fff;padding: 30px;}.text1 {font-size: 5em;text-shadow: -1px 1px #bbb, -2px 2px #bbb, -3px 3px #bbb, -4px 4px #bbb,-5px 5px #bbb, -10px 10px 3px #0008;}.text2 {font-weight: 700;position: relative;}.text2::after {content: "DARKNESS";position: absolute;left: 0;top: 0;color: #000;transform: translate(-25px, 2px) scale(0.9) skew(50deg);z-index:-1;filter: blur(2px);mask:linear-gradient(transparent,#000)}</style><body><h1 class="text1">立體文字</h1><h1 class="text2">DARKNESS</h1></body>
九、文字連續(xù)光影特效
span {color: #faebd7;animation: colorChange 1s infinite alternate;
}
@keyframes colorChange {to {color: #ff0266;}
}
@for $i from 1 through 7 {span:nth-child(#{$i}) {animation-delay: ($i - 1) * 0.1s;}
}
十、重復(fù)漸變的邊框
<style>.card {width: 217px;margin: 0 auto;color: #333;line-height: 1.8;border-radius: 10px;background: repeating-linear-gradient(-45deg,#e8544d 0 10px,#fff 10px 20px,#75adf8 20px 30px,#fff 30px 40px) -20px -20px/200% 200%;padding: 5px;transition: 0.5s;}.card:hover {background-position: 0 0;}.container {background: #fff;border-radius: inherit;}</style><body><div class="card"><div class="container">重復(fù)漸變的邊框原理:<br/>設(shè)置背景為重復(fù)的線性漸變。漸變角度為-45度,包含四個(gè)顏色區(qū)塊。</div></div></body>
十一、磨砂玻璃效果
<style>.wrap {text-align: center;color: white;}.modal {background: rgba(255, 255, 255, 0.4); /* 半透明背景 */backdrop-filter: blur(10px); /* 模糊背景 */border-radius: 15px; /* 圓角 */padding: 40px;width: 300px;box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); /* 增加陰影 */font-size: 24px;}</style><body><div class="wrap"><div class="modal">磨砂玻璃效果</div></div></body>
十二、各種FLIP動(dòng)畫
參考:https://zhuanlan.zhihu.com/p/712766286
<style>.box {width: 60px;height: 60px;background-color: skyblue;color: white;font-size: 30px;margin: 10px;}</style><body><div class="container" style="display: flex"><div class="box" key="1">1</div><div class="box" key="2">2</div><div class="box" key="3">3</div><div class="box" key="4">4</div><div class="box" key="5">5</div></div><button onclick="shuffle()">打亂</button><script>function shuffle() {const container = document.querySelector(".container");const boxes = Array.from(container.children);// First: 記錄每個(gè)盒子的起始位置const startPositions = boxes.reduce((result, box) => ({...result,[box.getAttribute("key")]: box.getBoundingClientRect(),}),{});// 隨機(jī)打亂盒子順序,然后把打亂好的盒子放回 DOMboxes.sort(() => Math.random() - 0.5);boxes.forEach((box) => container.appendChild(box));// Last: 記錄每個(gè)盒子的最終位置const endPositions = boxes.reduce((result, box) => ({...result,[box.getAttribute("key")]: box.getBoundingClientRect(),}),{});// Invert: 計(jì)算 “反向” 偏移量boxes.forEach((box) => {const key = box.getAttribute("key");const start = startPositions[key];const end = endPositions[key];// 注意,此時(shí) DOM 已經(jīng)處于最終位置,所以它的 translate 是 “反向” 的// 所以要用 first 來減去 lastconst deltaX = start.left - end.left;const deltaY = start.top - end.top;// 如果元素 “原地不動(dòng)”,那么跳過后續(xù)流程if (deltaX === 0 && deltaY === 0) {return;}// 讓元素通過 transform 偏移回到起點(diǎn)box.style.transition = null; // 暫時(shí)屏蔽掉過渡,實(shí)際生產(chǎn)此處需完善box.style.transform = `translate(${deltaX}px, ${deltaY}px)`;// Play: 在重繪之前,撤掉 transform 偏移,播放 “歸位” 過渡動(dòng)畫requestAnimationFrame(() => {box.style.transition = `transform 2s`;box.style.transform = "";});// FLIP 動(dòng)畫完成后,清理殘余樣式box.addEventListener("transitionend",() => {box.style.transition = null;box.style.transform = null;},{ once: true });});}</script></body>
其中的“Invert” 和 “Play” 步驟可以使用 Web Animation API 進(jìn)行簡化
<style>.box {width: 60px;height: 60px;color: white;font-size: 30px;margin: 10px;box-sizing: border-box;background-color: skyblue;border: 2px black solid;transition: width 500ms, height 500ms;}.scale {position: absolute;top: 90px;left: 10px;width: 120px;height: 120px;z-index: 10;}</style><body><div class="container" style="display: flex"><div class="box" key="1">1</div><div class="box" key="2">2</div><div class="box" key="3">3</div><div class="box" key="4">4</div><div class="box" key="5">5</div></div><script>const container = document.querySelector('.container')const boxes = Array.from(container.children)boxes.forEach(box => {box.addEventListener('click', () => {// First: 記錄每個(gè)盒子的起始位置const startPositions = boxes.reduce((result, box) => ({...result,[box.getAttribute('key')]: box.getBoundingClientRect(),}),{})box.classList.toggle('scale')// Last: 記錄每個(gè)盒子的最終位置const endPositions = boxes.reduce((result, box) => ({...result,[box.getAttribute('key')]: box.getBoundingClientRect(),}),{})// Invert: 計(jì)算 “反向” 偏移量boxes.forEach(box => {const key = box.getAttribute('key')const start = startPositions[key]const end = endPositions[key]// 注意,此時(shí) DOM 已經(jīng)處于最終位置,所以它的 transform 是 “反向” 的// 所以要用 first 來減去 lastconst deltaX = start.left - end.leftconst deltaY = start.top - end.top// 如果元素 “原地不動(dòng)”,那么跳過后續(xù)流程if (deltaX === 0 && deltaY === 0) {return}// 將盒子通過 transform 移至初始位置box.style.transition = ''box.style.transform = `translate(${deltaX}px, ${deltaY}px)`// Play: 播放動(dòng)畫應(yīng)用變換requestAnimationFrame(() => {box.style.transition = `all 500ms`box.style.transform = ''})// FLIP 動(dòng)畫完成后,清理殘余樣式box.addEventListener('transitionend',() => {box.style.transition = nullbox.style.transform = null},{ once: true })})})})</script></body>
Vue 內(nèi)置組件?<TransitionGroup>
?已經(jīng)實(shí)現(xiàn)了 FLIP 動(dòng)畫
<template><TransitionGroup style="display: flex" name="flip" tag="div"><div class="box" v-for="item of list" :key="item">{{ item }}</div></TransitionGroup><button @click="shuffle">打亂</button>
</template>
<script setup>
import { reactive } from 'vue'
const list = reactive([1, 2, 3, 4, 5])
const shuffle = () => void list.sort(() => Math.random() - 0.5)
</script>
<style scoped>
.box {width: 60px;height: 60px;background-color: skyblue;color: white;font-size: 30px;margin: 10px;
}
.flip-move {transition: all 2s;
}
</style>
react-flip-toolkit
?工具是一個(gè)用于實(shí)現(xiàn)組件 FLIP 動(dòng)畫的 React 庫。