鐵人挑戰終於快到尾聲了…今天就來調整一些專案的小細節吧:
因為中文一個字是2個 byte,英文一個字母是1個 byte,但 .length
卻會將中文字當作1個字來回傳,這樣會造成我們在使用 .splice()
、.substring()
時計算長度錯誤,為了解決這種情況,我們可以另外寫一個函數來處理:
utils/formatter.js
中新增 subString
函數str
(要判斷的字串)、n
(要裁切的長度)str
長度是否大於 n
,若小於則回傳字串 str
len
長度+2,反之則 len
+1(使用 RegExp.test()
來判斷字元是否為中文(符合表達式))len > n
,則跳出迴圈,反之則將當前字元加入暫時存放字串的 tmpStr
n
長度的字串 tmpStr
並加上 「…」export const subString = (str, n) => {
if (str.replace(/[\u4e00-\u9fa5]/g, '**').length <= n) {
return str;
} else {
let len = 0;
let tmpStr = '';
for (let i = 0; i < str.length; i++) {
if (/[\u4e00-\u9fa5]/.test(str[i])) {
len += 2;
} else {
len += 1;
}
if (len > n) {
break;
} else {
tmpStr += str[i];
}
}
return tmpStr + ' ...';
}
};
接著一樣匯入使用即可!
<div className="">{loading ? <Skeleton count={3} /> : subString(el.content, 120)}</div>
目前的 Header 背景設定是透明的,我們可以定義一個狀態變數 isScrollDown
並搭配 window.addEventListener()
來監控使用者是否有往下瀏覽視窗,然後依據條件來渲染不同的樣式
const [isScrollDown, setIsScrollDown] = useState(false);
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
const handleScroll = () => {
if (window.scrollY >= 80) {
setIsScrollDown(true);
} else {
setIsScrollDown(false);
}
};
接著在 className 用樣板字串``
包起來,放入條件判斷來更改樣式
<div
className={`sticky top-0 z-50 w-screen h-20 text-xl flex flex-row items-center px-14 mb-14 transition ease-in ${
isScrollDown ? 'bg-yellow-50 h-16' : 'bg-transparent border-b border-b-yellow-700/50'
}`}
>
...
</div>
建立 components/BackToTop.js
檔案,大致上跟 Header 相同概念,在 onClick
時呼叫 scrollToTop
→ 將視窗移至最上方 window.scrollTo(0, 0)
import React, { useState, useEffect } from 'react';
import { ArrowUpCircleIcon } from '@heroicons/react/24/solid';
export default function BackToTop() {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
const handleScroll = () => {
if (window.scrollY > window.innerHeight / 5) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};
const scrollToTop = () => {
window.scrollTo(0, 0);
};
return (
<div>
{isVisible && (
<div
className="fixed bottom-16 right-16 text-yellow-700/50 hover:cursor-pointer hover:text-yellow-700/80"
onClick={scrollToTop}
>
<ArrowUpCircleIcon className="w-14 h-14" />
</div>
)}
</div>
);
}