新聞中心
我剛接觸前端這個行業(yè)的時候就有一個想法,那就是寫一個超炫酷的圖片預(yù)覽畫廊。還記得當時用美圖看看看,那輕快的速度和交互很是令人著迷。

創(chuàng)新互聯(lián)服務(wù)項目包括白山網(wǎng)站建設(shè)、白山網(wǎng)站制作、白山網(wǎng)頁制作以及白山網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,白山網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到白山省份的部分城市,未來相信會繼續(xù)擴大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!
該組件在幾年前已經(jīng)發(fā)布不完全版,后面斷斷續(xù)續(xù)的維護,總感覺差了點什么。今年春節(jié)沒休息,全搭在上面進行開發(fā),現(xiàn)在總算是完美實現(xiàn)!先看看效果:
縮略圖完美漸變:
指定位置放大:
減速滾動:
什么是 react-photo-view
react-photo-view 擁有無與倫比的預(yù)覽交互體驗:從打開圖像開始,每一幀的動畫、細節(jié)和交互都經(jīng)過了精心設(shè)計與反復(fù)調(diào)試,媲美原生圖片預(yù)覽的效果。
pnpm i react-photo-view
概覽:
import { PhotoProvider, PhotoView } from'react-photo-view';
import'react-photo-view/dist/react-photo-view.css';
exportdefaultfunction MyComponent() {
return (

);
}
為什么要單獨開發(fā)它?
當然想實現(xiàn)它的執(zhí)念也算一個方面,但根本原因是在 React 強大的生態(tài)中根本找不到一個好用的圖片預(yù)覽方案。當時奉行拿來主義,在網(wǎng)上找了一圈基于 React 放大預(yù)覽組件庫,結(jié)果令我有點意外,圖片放大預(yù)覽的庫的數(shù)量明顯比不上輪播組件庫。更令人窒息的是這些少得可憐的組件庫中,其中一大半都是基于 PhotoSwipe 這個開源庫進行的二次封裝。除此之外,能用于實際生產(chǎn)的預(yù)覽組件庫……好像沒有(也可能是我找不到),這種情況不僅體現(xiàn)在 React 庫上,其他框架 Vue 乃至是原生的相關(guān)庫都是如此。
當然 PhotoSwipe 也不是不能用,但原生操作 DOM 的寫法在 React 中格格不入,其體積也是在 gzip 12KB 之上了,顯得有點臃腫了,便有了這個大膽的想法。
它有多優(yōu)秀?
它擁有非常完善的細節(jié)與特性:
- 支持觸摸手勢,拖動/平移/物理效果滑動,雙指指定位置放大/縮小
- 全方面動畫銜接,打開/關(guān)閉/回彈/觸邊,順其自然的交互效果
- 圖像自適應(yīng),以一個合適的最初呈現(xiàn)大小,并根據(jù)調(diào)整自適應(yīng)
- 支持自定義如 或任意 HTML 元素的預(yù)覽
- 鍵盤導(dǎo)航,完美適配桌面端
- 支持自定義節(jié)點擴展,輕松實現(xiàn)全屏預(yù)覽、旋轉(zhuǎn)控制、圖片介紹以及更多功能
- 基于 typescript,7KB Gzipped,支持服務(wù)端渲染
- 簡單易用的 API,上手零成本
還導(dǎo)出了支持 ES2017 以上的 JS,可以做到 6KB Gzipped。在如此的體積上加上非常多的體驗細節(jié)實屬不容易,更多的功能可以通過非常容易的自定義渲染來實現(xiàn),這與 React 理念完美契合,從而可以避免內(nèi)置一些非剛需的功能。
流行庫對比
以下表格統(tǒng)計了大部分場景所需功能,展示 react-photo-view 、 PhotoSwipe 和 rc-image(ant-design) 對比:
友好的文檔
還有什么比文檔更重要了,為此,我還準備了一個超漂亮的文檔(目前只有中文,以后有時間在翻譯吧~)
https://react-photo-view.vercel.app/ 文末查看原文可預(yù)覽
實現(xiàn)歷程
圖片跟隨手指滾動
在 onTouchStart 時記錄當前觸發(fā)位置狀態(tài),在 onTouchMove 時讓其跟隨手指移動,onTouchEnd 解除跟隨就可以簡單實現(xiàn)。
觸邊位置反饋使圖片切換都是需要慢慢琢磨細節(jié):在 onTouchStart 之后移動如果立即讓圖片跟隨手指移動的話會帶來許多誤操作,比如本想讓他切換圖片卻走了上下滑動的邏輯。這時候就需要一個 20px 的移動緩沖來預(yù)判手指移動方向。
指定圖片位置進行放大
使用 transform: scale(value) 可以實現(xiàn)對圖片的縮放,但是都是對圖片中心進行放大,縮放的結(jié)果可能不是想要的。起初打算用 transform-origin 來實現(xiàn),想法是美好的,雖然第一次在指定的位置能夠進行放大。倘若縮小的位置不是原來的位置就會產(chǎn)生混亂跳動,很顯然這個方式不行。
后來思來想去睡不著,在睡夢中發(fā)現(xiàn)了靈感:便于計算理解,我們設(shè)圖片中心點為 0, 任何指定位置的放大縮小,即改變圖片中心的位置。比如圖片寬度 200,中心點位置為 100,基于最左側(cè)位置放大一倍?,F(xiàn)在圖片寬度 400,那么中心點的位置應(yīng)為 200。那么總結(jié)公式如下:
const centerClientX = innerWidth / 2;
// 坐標偏移轉(zhuǎn)換
const lastPositionX = centerClientX + lastX;
// 縮放偏移
const offsetScale = nextScale / scale;
// 最終偏移位置
const originX = clientX - (clientX - lastPositionX) * offsetScale - centerClientX;
這種模式計算能承擔各種位置響應(yīng),比如雙指縮放、雙指滾動+縮放、邊緣計算等等。
雙指之間的距離
這里需要初中時直角三角勾股定理:
Math.sqrt((nextClientX - clientX) ** 2 + (nextClientY - clientY) ** 2);
模擬滾動操作
之前的版本使用 transition 實現(xiàn),通過手指滑動開始結(jié)束的時間差,計算出初始速度,估摸著用 transition 模擬出一個距離讓眼睛看起來有滾動效果 。但這種方式體驗始終差很多。后面結(jié)合高中物理公式模擬出滾動效果:
加速運動:
空氣阻力:
CρS 都是常數(shù),干脆都搞成一個量好了。至于怎么出這個量大小……試出來的 這樣就只與 v 平方成正比了。
另外因為和運動方向相反,取個 v 的方向即 Math.sign(-v)
function scrollMove(
initialSpeed: number,
callback: (spatial: number) => boolean,
) {
// 加速度
const acceleration = -0.002;
// 阻力
const resistance = 0.0002;
let v = initialSpeed;
let s = 0;
let lastTime: number | undefined = undefined;
let frameId = 0;
const calcMove = (now: number) => {
if (!lastTime) {
lastTime = now;
}
const dt = now - lastTime;
const direction = Math.sign(initialSpeed);
const a = direction * acceleration;
const f = Math.sign(-v) * v ** 2 * resistance;
const ds = v * dt + ((a + f) * dt ** 2) / 2;
v = v + (a + f) * dt;
s = s + ds;
// move to s
lastTime = now;
if (direction * v <= 0) {
cancelAnimationFrame(frameId);
return;
}
if (callback(s)) {
frameId = requestAnimationFrame(calcMove);
return;
}
cancelAnimationFrame(frameId);
};
frameId = requestAnimationFrame(calcMove);
}
縮略圖裁切
PhotoSwipe 支持縮略圖裁切,不過需要手動指定圖片寬高和 data-cropped,相當麻煩。react-photo-view 通過讀取縮略圖 getComputedStyle(element).objectFit 來獲取當前裁切參數(shù)。實現(xiàn)自動裁切效果。
兼容性處理
因為每張圖片都是一個合成層,這會消耗相當多的內(nèi)存。IOS 上對于內(nèi)存有相當大的限制,如果圖片在放大的情況一直使用 scale,那么在 Safari 上會顯得非常模糊?,F(xiàn)在通過每次在運動完成后,都改變圖片的寬高為指定的值,然后重設(shè) scale 為 1,這種方式應(yīng)該本身需要達到的效果吧。
網(wǎng)頁標題:2022強力之作:一款超精致的圖片預(yù)覽組件
URL分享:http://fisionsoft.com.cn/article/coshdsc.html


咨詢
建站咨詢
