Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
Threejs(下面簡稱 Three) 作為一個 3D 庫,不僅減少了我們學習 OpenGL 和 WebGL 的成本,還大大提升了前端在可視化上給用戶帶來更多的真實、沉浸式的體驗。眾所周知,Three 更多的是用 3D 模型 + 投影相機 + 用戶交互的方式來構建一個「3D 世界」。
這張專輯,用眼睛去“聽” 活動中,在視覺在只能提供「2D 切圖」的情況下,需要營造「3D 效果」。為了獲得最好視覺體驗,僅僅通過貼圖很難做到,所以借此機會探索了 Three 的動效方案。
運動往往是相對的,運動的本質可能是「物體動」或「相機動」,本文將從對象動畫和相機動畫上闡述對 Three 的動效探索。
Three 提供多種相機,其中應用最廣的就是投影相機 (PerspectiveCamera) ,通過投影相機可以模擬人眼所看見的效果。
const camera=THREE.PerspectiveCamera(fov, aspect, near, far);
復制代碼
參數 含義 默認值 fov fov 是視景體豎直方向上(非水平!)的張角,人類有接近180度的視角大小。該值可根據具體場景所需要的視角設置。 45 aspect 指定渲染結果的橫向尺寸和縱向尺寸的比值。該值通常設置為窗口大小的寬高比。 window.innerWidth / window.innerHeight near 表示可以看到多近的物體。這個值通常很小。 0.1 far 表示可以看到多遠的物體。這個看情況設置,過大會導致渲染過多;太小可能又會看不到。 1000
ps: 在 Three 中是沒有「長度單位」這個概念的,它的數值都是根據比例計算得出,因此這里提到的 0.1 或 1000 都沒有具體的含義,而是一種相對長度。
可以看到,通過配置透視相機的相關參數,最終被渲染到屏幕上的,是在 near 到 far 之間,根據 fov 的值和物體遠近 d 確定渲染高度,再通過 aspect 值來確定渲染寬度的。
有了相機,我們還要有場景,場景是為了讓我們設置我們的空間內「有什么」和「放在哪」的。我們可以在場景中放置物體,光源還有相機。
const scene=new THREE.Scene();
復制代碼
是的,創建場景就是這么簡單。
為了以群的維度去區分場景中的物體,我們還可以在場景中添加 Group。有了 Group,可以更方便地操作一類物體。 比如創建一個 stoneGroup,并添加到場景中:
const stoneGroup=new THREE.Group();
stoneGroup.name='stoneGroup';
scene.add(stoneGroup);
復制代碼
為 Group 命名,允許我們通過 name 來獲取到對應的 Group:
const group=scene.getObjectByName(name);
復制代碼
Three 提供了多種類型的幾何體,可以分為二維網格和三維網格。二維網格顧名思義只有兩個維度,可以通過這種幾何體創建簡單的二維平面;三維網格允許你定義三維物體;在 Three 中定義一個幾何體十分簡單,只需要選擇需要的幾何體并傳入相應參數創建即可。
查看Three提供的幾何體
如果看到 Three 提供的幾何體,可以看到有的幾何體中它分別提供 Geometery 和 BufferGeometery 版本,關于這兩個的區別,可以看這里 回答
大致意思就是使用 Buffer 版本的幾何體相較于普通的幾何體會將描述物體的數據存放在緩沖區中,減少內存消耗和 CPU 循環。通過它們提供的方法來看,使用 geometry 無疑是對新手友好的。
創建幾何體:
// 創建立方體,傳入長、寬和高
var cubeGeometry=new THREE.CubeGeometry(40, 40, 40);
// 創建球體,傳入半徑、寬片段數量和高片段數量
var sphereGeometry=new THREE.SphereGeometry(20, 100, 100);
復制代碼
定義材質可以幫助我們決定一個物體在各種環境情況下的具體表現。同樣 Three 也提供了多種材質。下面列舉幾個常用的材質。
名稱 描述 MeshBasicMaterial 基礎材質,用它定義幾何體上的簡單顏色或線框 MeshPhongMaterial 受光照影響,用來創建光亮的物體 MeshLambertMaterial 受光照影響,用來創建不光亮的物體 MeshDepthMaterial 根據相機遠近來決定如何給網格染色
創建材質:
var basicMaterial=new THREE.MeshBasicMaterial({ color: 0x666666 });
var lambertMaterial=new THREE.MeshLambertMaterial({ color: 0x666666 });
var phongMaterial=new THREE.MeshPhongMaterial({ color: 0x666666 });
var wireMaterial=new THREE.MeshBasicMaterial({ wireframe: true, color: 0x666666 });
復制代碼
更多材質和相關信息,可以查看 材質
需要添加到場景中,還需要依賴 Mesh。Mesh 是用來定義材質和幾何體之間是如何粘合的,創建網格對象可以應用一個或多個材質和幾何體。
創建幾何體相同材質不同的網格對象:
var cube=new THREE.Mesh(cubeGeometry, basicMaterial);
var cubePhong=new THREE.Mesh(cubeGeometry, phongMaterial);
scene.add(cube, cubePhong);
復制代碼
創建材質相同幾何體不同的網格對象:
var cube=new THREE.Mesh(cubeGeometry, basicMaterial);
var sphere=new THREE.Mesh(sphereGeometry, basicMaterial);
scene.add(cube, sphere);
復制代碼
創建擁有多個材質幾何體的網格對象:
var phongMaterial=new THREE.MeshPhongMaterial({ color: 0x666666 });
var cubeMeshPhong=new THREE.Mesh(cubeGeometry, cubePhongMaterial);
var cubeMeshWire=new THREE.Mesh(cubeGeometry, wireMaterial);
// 網格對象新增材質
cubeMeshPhong.add(cubeMeshWire);
scene.add(cubeMeshPhong);
復制代碼
有了場景和相機,我們還需要渲染器把對應的場景用對應的相機可見渲染出來,因此渲染器需要傳入場景和相機參數。
// 抗鋸齒、canvas 是否支持 alpha 透明度、preserveDrawingBuffer 是否保存 BUFFER 直到手動清除
const renderer=new THREE.WebGLRenderer({
antialias: true, alpha: true, preserveDrawingBuffer: true
});
renderer.setSize(this.width, this.height);
renderer.autoClear=true;
// 清除顏色,第二個參數為 0 表示完全透明,適用于需要透出背景的場景
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(window.devicePixelRatio);
復制代碼
為了在相機更新后所看見的場景,需要在循環渲染中加上
renderer.render(scene, camera);
復制代碼
有了相機場景和渲染器,我們已經可以看到初步的效果了。但3D世界里,靜止的物體多無趣啊。于是我們嘗試加入動畫效果。
Three為動畫提供了一系列方法。
參數 含義 AnimationMixer 作為特定對象的動畫混合器,可以管理該對象的所有動畫 AnimationAction 為播放器指定對應的片段存儲一系列行為,用來指定動畫快慢,循環類型等 AnimationClip 表示可重用的動畫行為片段,用來指定一個動畫的動畫效果(放大縮小、上下移動等) KeyframeTrack 與時間相關的幀序列,傳入時間和值,應用在指定對象的屬性上。目前有 BooleanKeyframeTrack VectorKeyframeTrack 等。
那么如何創建一個動畫呢?下面這個例子給大家解釋如何讓網格對象進行簡單的上下移動。
創建特定對象的動畫混合器:
// 創建紋理
const texture=new THREE.TextureLoader().load(img.src);
// 使用紋理創建貼圖
const material=new THREE.SpriteMaterial({ map: texture, color: 0x666666 });
// 使用貼圖創建貼圖對象
const stone=new THREE.Sprite(material);
// 為貼圖對象創建動畫混合器
const mixer=new THREE.AnimationMixer(stone);
復制代碼
創建動畫行為片段:
const getClip=(pos=[0, 0, 0])=> {
const [x, y, z]=pos;
const times=[0, 1]; // 關鍵幀時間數組,離散的時間點序列
const values=[x, y, z, x, y + 3, z]; // 與時間點對應的值組成的數組
// 創建位置關鍵幀對象:0時刻對應位置0, 0, 0 10時刻對應位置150, 0, 0
const posTrack=new THREE.VectorKeyframeTrack('stone.position', times, values);
const duration=1;
return new THREE.AnimationClip('stonePosClip', duration, [posTrack]);
};
復制代碼
創建動畫播放器,確定動畫的表現:
const action=mixer.clipAction(getClip([x, y, z]));
action.timeScale=1; // 動畫播放一個周期的時間
action.loop=THREE.LoopPingPong; // 動畫循環類型
action.play(); // 播放
復制代碼
在循環繪制中更新混合器,保證動畫的執行:
animate() {
// 更新動畫
const delta=this.clock.getDelta();
mixer.update(delta);
requestAnimationFrame(()=> {
animate();
});
}
復制代碼
codepen
有了 Animation 我們可以很簡單地對物體的一些屬性進行操作。但一些貼圖相關的動畫就很難用 Animation 來實現了,比如:
上圖這種,無法通過改變物體的位置、大小等屬性實現。于是,還有一種方案 —— 貼圖動畫。
類似在 CSS3 中對序列圖片使用 transform 屬性改變位置來達到的動畫效果,實際上在 Three 中也可以使用貼圖位移的方式實現。
首先,我們要有一個序列圖:
作為紋理加載,并且增加到場景中:
const arrowTexture=new THREE.TextureLoader().load(Arrow);
const material=new THREE.SpriteMaterial({ map: arrowTexture, color: 0xffffff });
const arrow=new THREE.Sprite(material);
scene.add(arrow);
復制代碼
聲明 TextAnimator 對象,實現紋理的位移:
function TextureAnimator(texture, tilesHoriz, tilesVert, numTiles, tileDispDuration) {
// 紋理對象通過引用傳入,之后可以直接使用update方法更新紋理位置
this.tilesHorizontal=tilesHoriz;
this.tilesVertical=tilesVert;
// 序列圖中的幀數
this.numberOfTiles=numTiles;
texture.wrapS=THREE.RepeatWrapping;
texture.wrapT=THREE.RepeatWrapping;
texture.repeat.set(1 / this.tilesHorizontal, 1 / this.tilesVertical);
// 每一幀停留時長
this.tileDisplayDuration=tileDispDuration;
// 當前幀停留時長
this.currentDisplayTime=0;
// 當前幀
this.currentTile=0;
// 更新函數,通過這個函數對紋理位移進行更新
this.update=(milliSec)=> {
this.currentDisplayTime +=milliSec;
while (this.currentDisplayTime > this.tileDisplayDuration) {
this.currentDisplayTime -=this.tileDisplayDuration;
this.currentTile++;
if (this.currentTile===this.numberOfTiles) { this.currentTile=0; }
const currentColumn=this.currentTile % this.tilesHorizontal;
texture.offset.x=currentColumn / this.tilesHorizontal;
const currentRow=Math.floor(this.currentTile / this.tilesHorizontal);
texture.offset.y=currentRow / this.tilesVertical;
}
};
}
復制代碼
// 傳入一個一行里有 13 幀的序列圖,每張序列圖停留 75ms
const arrowAni=new TextureAnimator(arrowTexture, 13, 1, 13, 75);
復制代碼
在循環繪制中更新,保證動畫的執行:
arrowAni.update(delta);
復制代碼
作為引用傳入后,對貼圖的修改會直接體現在使用該貼圖的材質上。
codepen
Three 中還提供了酷炫的粒子動畫,使用繼承自 Object3D 的 Points 類實現。有了 Points 類我們可以很方便地把一個幾何體渲染成一組粒子,并對它們進行控制。
創建粒子我們首先需要創建粒子的材質,可以使用 PointsMaterial 創建粒子材質。
const texture=new THREE.TextureLoader().load('https://p1.music.126.net/jgzbZtWZhDet2jWzED8BTw==/109951164579600342.png');
material=new THREE.PointsMaterial({
color: 0xffffff,
// 映射到材質上的貼圖
map: texture,
size: 2,
// 粒子的大小是否和其與攝像機的距離有關,默認值 true
sizeAttenuation: true,
});
// 開啟透明度測試,透明度低于0.5的片段會被丟棄,解決貼圖邊緣感問題
material.alphaTest=0.5;
復制代碼
有了粒子材質后,我們可以應用同一個材質批量創建一組粒子,只需要傳入一個簡單的幾何體。
var particles=new THREE.Points( geometry, material );
復制代碼
如果你傳入的是 BoxGeometry 你可能會得到這樣的一組粒子
還可以根據傳入的 Shape 得到這樣一組粒子
但有趣的粒子絕不是靜止的,而是有活動、有過程的。但如果自己動手實現一個粒子的運動又很復雜,因此希望借助一些第三方庫實現粒子動畫的緩動過程。
tween.js 是一個小型的 JS 庫,我們可以使用它為我們的動畫聲明變化。使用 tween.js 我們不需要關心運動的中間狀態,只需要關注粒子的:
// srcPosition, targetPosition;
tweens.push(new TWEEN.Tween(srcPosition).easing(TWEEN.Easing.Exponential.In));
// tweens最終位置、緩動時間
tweens[0].to(targetPosition, 5000);
tweens[0].start();、
復制代碼
codepen
其實粒子動畫的場景還有很多,我們可以用他們創造雪花飄散、穿梭效果,本質都是粒子的位置變化。
相機在 3D 空間中充當人的眼睛,因此自然的相機動線可以保證交互的自然流暢。
Three 提供了一系列相機控件來控制場景中的相機軌跡,這些控件適用于大部分場景。使用 Controls 開發者可以不再需要去關心用戶交互和相機移動的問題。
活動中也涉及到 OrbitControls 的使用,他提供了環繞物體旋轉、平移和縮放的方法,但由于對使用二維貼圖的情況下,旋轉和縮放都容易穿幫,需要被禁止。
// 創建軌跡
const controls=new THREE.OrbitControls(this.camera, this.renderer.domElement);
controls.enabled=!0;
controls.target=new THREE.Vector3();
controls.minDistance=0;
controls.maxDistance=2000;
controls.minPolarAngle=Math.PI / 2;
controls.maxPolarAngle=Math.PI / 2;
// 禁用縮放
controls.enableZoom=!1;
// 禁用旋轉
controls.enableRotate !1;
controls.panSpeed=2;
// 修改控件的默認觸摸選項,設置為單指雙指都為平移操作
controls.touches={
ONE: THREE.TOUCH.PAN,
TWO: THREE.TOUCH.PAN,
};
this.scene.add(this.camera);
復制代碼
OrbitControl 還允許我們設置阻尼,設置該值表現為數值越接近 1 越難拖動,開啟阻尼后需要我們手動 update 控件。
controls.enableDamping=!0;
controls.dampingFactor=0.2;
復制代碼
查看源碼可以看到,阻尼的實現就是依賴滑動時的 offset 乘上一個權重,在通過后續的update不斷為 panOffset 乘上一個權重實現滑動難,撒手后再滑動一點距離。
// this method is exposed, but perhaps it would be better if we can make it private...
this.update=function () {
// ...
return function update() {
// ...
// 平移
if ( scope.enableDamping===true ) {
// 開啟阻尼后會在原本的位移上乘上一個權重
scope.target.addScaledVector( panOffset, scope.dampingFactor );
} else {
scope.target.add( panOffset );
}
// ...
if ( scope.enableDamping===true ) {
sphericalDelta.theta *=( 1 - scope.dampingFactor );
sphericalDelta.phi *=( 1 - scope.dampingFactor );
// 如果沒有人為操作,隨著時間推移,panOffset會越來越小
panOffset.multiplyScalar( 1 - scope.dampingFactor );
} else {
sphericalDelta.set( 0, 0, 0 );
panOffset.set( 0, 0, 0 );
}
// ...
};
}();
復制代碼
官方也提供了 Controls 的 例子 供大家參考。
如果不使用 Controls,僅僅是相機從一個點移動到另一個點,為了更平滑自然的相機軌跡,推薦使用貝塞爾曲線。
貝塞爾曲線是一個由起點、終點和控制點決定的一條時間相關的變化曲線。這里以二階貝塞爾曲線為例,實現相機的曲線移動。(三維的點有點難說明白,這里用二維坐標來解釋)
上圖中小黑點的移動軌跡可以看做相機移動的曲線。
從該公式來看,只需要確定 p0、p1 和 p2 三個點,在單位時間下我們可以獲得一條確定的曲線。
但是,換成坐標點要怎么做呢?
// 獲得貝塞爾曲線
function getBezier(p1, p2) {
// 在指定范圍內隨機生成一個控制點
const cp={
x: p1.x + Math.random() * 100 + 200,
z: p2.z + Math.random() * 200,
};
let t=0;
// 貝塞爾曲線公式,根據時間確定點的位置
return (deltat)=> {
if (t >=1) return [p2.x, p2.y];
t +=deltat;
if (t > 1) t=1;
const { x: x1, z: z1 }=p1;
const { x: cx, z: cz }=cp;
const { x: x2, z: z2 }=p2;
const x=(1 - t) * (1 - t) * x1 + 2 * t * (1 - t) * cx + t * t * x2;
const z=(1 - t) * (1 - t) * z1
+ 2 * t * (1 - t) * cz + t * t * z2;
return [x, z];
};
}
復制代碼
const bezier=getBezier(p1, p2);
復制代碼
為了從簡,這里只實現了二維坐標的軌跡變化,但三維也是同理。
因為貝塞爾曲線是時間相關曲線,在每一次循環渲染中要傳入時間來更新相機位置。
animation() {
const [x, z]=bezier(clock.getDelta());
camera.position.x=x;
camera.position.z=z;
requestAnimationFrame(()=> {
animate();
});
}
復制代碼
沒趕上 Three 的熱潮,只能趁著活動需求給自己補補課了。在三維空間中,動畫能夠讓空間中的物體更加生動,而相機的移動帶給用戶更強的空間感。
本文介紹了基于 Animation 實現物體的簡單運動、 Texture 實現貼圖動畫以及使用 Points 粒子化的物體動畫方案;基于 Controls 和貝塞爾曲線的相機動畫方案。
對 Three 有興趣的朋友,可以通過 官方文檔 來學習,里面提供的例子覆蓋了大部分場景。
以上是我在活動中涉及到的一些動畫方案,難免會出現理解偏差和表達錯誤,如果有更多的動效方案歡迎一起探討~
家好,很高興又見面了,我是"高級前端?進階?",由我帶著大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發,您的支持是我不斷創作的動力。
今天給大家帶來的主題是HTML5 和word的互相轉化,話不多說,直接進入正題!
html-docx-js 是一個非常小的庫,能夠將 HTML 文檔轉換為 Microsoft Word 2007 及更高版本使用的 DOCX 格式。 html-docx-js 設法使用稱為“altchunks”的功能在瀏覽器中執行轉換。 簡而言之,它允許以不同的標記語言嵌入內容。 開發者使用 MHT 文檔將嵌入內容發送到 Word,因為它允許處理圖像。 Word 打開此類文件后,會將外部內容轉換為 Word Processing ML(這是 DOCX 文件的標記語言的調用方式)并替換引用。
Microsoft Word for Mac 2008 不支持 Altchunk,LibreOffice 和 Google Docs 也不支持 Altchunk。
關于 html-docx-js 庫有幾點需要說明:
目前 Mammoth 在 Github 上通過 MIT 協議開源,有超過 1k 的 star、0.3k 的 fork、0.7k 的項目依賴量、NPM 周平均下載量 9k,是一個值得關注的前端開源項目。
var converted=htmlDocx.asBlob(content);
saveAs(converted, "test.docx");
asBlob 可以采用其他選項來控制文檔的頁面設置:
比如下面的例子:
var converted=htmlDocx.asBlob(content, {
orientation: "landscape",
margins: { top: 720 },
});
saveAs(converted, "test.docx");
需要注意的是,開發者需要傳遞完整、有效的 HTML(包括 DOCTYPE、html 和 body 標簽)。 這可能不太方便,但可以讓開發者在樣式標簽中包含 CSS 規則。
html-docx-js 作為獨立”Browserify 模塊(UMD)分發。 開發者可以將其作為 html-docx 要求。 如果沒有可用的模塊加載器,它將把自己注冊在 window.htmlDocx。
Mammoth.js 旨在轉換 .docx 文檔,例如:由 Microsoft Word、Google Docs 和 LibreOffice 創建的文檔,并將其轉換為 HTML。 Mammoth 的目標是通過使用文檔中的語義信息并忽略其他細節來生成簡單且干凈的 HTML。 例如,Mammoth 將任何具有標題 1 樣式的段落轉換為 h1 元素,而不是嘗試精確復制標題的樣式(字體、文本大小、顏色等)。
.docx 使用的結構與 HTML 的結構之間存在很大的不匹配,這意味著對于更復雜的文檔來說,轉換不太可能完美。 如果開發者僅使用樣式來對文檔進行語義標記,那么 Mammoth 效果最佳。
Mammoth.js 目前支持以下功能:
Mammoth 在眾多平臺可用,比如:Python、WordPress、Java/JVM、.NET 等等。目前 Mammoth 在 Github 上通過 BSD-2-Clause 開源,有超過 4.1k 的 star、0.5k 的 fork、4.4k 的項目依賴量、NPM 周平均下載量 76k,是一個值得關注的前端優質開源項目。
以文檔轉換為例。
Mammoth 允許在轉換文檔之前對其進行處理。 例如,假設該文檔尚未進行語義標記,但開發者知道任何居中對齊的段落都應該是標題,則可以使用 transformDocument 參數來適當地修改文檔:
function transformElement(element) {
if (element.children) {
var children=_.map(element.children, transformElement);
element={ ...element, children: children };
}
if (element.type==="paragraph") {
element=transformParagraph(element);
}
return element;
}
function transformParagraph(element) {
if (element.alignment==="center" && !element.styleId) {
return { ...element, styleId: "Heading2" };
} else {
return element;
}
}
var options={
transformDocument: transformElement,
};
TransformDocument 的返回值在 HTML 生成期間使用。同時,上面的代碼可以使用 mammoth.transforms.paragraph 函數進行優化,比如:
function transformParagraph(element) {
if (element.alignment==="center" && !element.styleId) {
return { ...element, styleId: "Heading2" };
} else {
return element;
}
}
var options={
transformDocument: mammoth.transforms.paragraph(transformParagraph),
};
或者,如果開發者希望已明確設置為使用等寬字體來表示代碼的段落:
const monospaceFonts=["consolas", "courier", "courier new"];
function transformParagraph(paragraph) {
var runs=mammoth.transforms.getDescendantsOfType(paragraph, "run");
var isMatch=runs.length > 0 &&
runs.every(function (run) {
return run.font && monospaceFonts.indexOf(run.font.toLowerCase()) !==-1;
});
if (isMatch) {
return {
...paragraph,
styleId: "code",
styleName: "Code",
};
} else {
return paragraph;
}
}
var options={
transformDocument: mammoth.transforms.paragraph(transformParagraph),
styleMap: ["p[style-name='Code']=> pre:separator('\n')"],
};
關于 Mammoth 庫的更多用法,更多 API 示例可以參考文末資料,本文不再過多展開。
本文主要和大家介紹 HTML5 和word互轉的兩個開源庫,分別為:html-docx-js、mammoth.js。相信通過本文的閱讀,大家對 html-docx-js、mammoth.js 會有一個初步的了解。
因為篇幅有限,關于 html-docx-js、mammoth.js 的更多用法和特性文章并沒有過多展開,如果有興趣,可以在我的主頁繼續閱讀,同時文末的參考資料提供了大量優秀文檔以供學習。最后,歡迎大家點贊、評論、轉發、收藏,您的支持是我不斷創作的動力。
https://github.com/evidenceprime/html-docx-js
https://github.com/mwilliamson/mammoth.js
https://www.npmjs.com/package/html-docx-js
https://www.npmjs.com/package/mammoth
https://www.tutorialswebsite.com/export-html-to-word-document-with-javascript/
https://www.vecteezy.com/vector-art/136754-free-vector-documents
擊右上方紅色按鈕關注“web秀”,讓你真正秀起來
在css3到來之前,都是用js來操作dom元素,計算位置,大小,形成瀑布流布局。但是有了css3之后,一切實現起來就太簡單了,沒有復雜的邏輯,輕松的幾行樣式代碼就可以搞定。
基于waterfall.js(11.8kb),還得寫入基礎的樣式,初始化等等,對比其他js,已經是很簡單了。
var waterfall=new WaterFall({ container: '#waterfall', pins: ".pin", loader: '#loader', gapHeight: 20, gapWidth: 20, pinWidth: 216, threshold: 100 });
但是,有了css3,再簡潔的js,都比不過簡單的css代碼。
關鍵點,橫向 flex 布局嵌套多列縱向 flex 布局,使用了 vw 進行自適應縮放
// pug 模板引擎 div.g-container -for(var i=0; i<4; i++) div.g-queue -for(var j=0; j<8; j++) div.g-item
不熟悉pug模板(jade)的,可以先去了解一下。其實看大致也就懂了,就是循環多個元素,沒有復雜的邏輯。
$lineCount: 4; $count: 8; // 隨機數(瀑布流某塊的高度) @function randomNum($max, $min: 0, $u: 1) { @return ($min + random($max)) * $u; } // 隨機顏色值 @function randomColor() { @return rgb(randomNum(255), randomNum(255), randomNum(255)); } .g-container { display: flex; flex-direction: row; justify-content: space-between; overflow: hidden; } .g-queue { display: flex; flex-direction: column; flex-basis: 24%; } .g-item { position: relative; width: 100%; margin: 2.5% 0; } @for $i from 1 to $lineCount+1 { .g-queue:nth-child(#{$i}) { @for $j from 1 to $count+1 { .g-item:nth-child(#{$j}) { height: #{randomNum(300, 50)}px; background: randomColor(); // 瀑布流某塊中間的數字 &::after { content: "#{$j}"; position: absolute; color: #fff; font-size: 24px; // 水平垂直居中 top: 50%; left: 50%; transform: translate(-50%, -50%); } } } } }
預覽:
CSS 實現瀑布流布局(display: flex)
演示地址: 點擊文章結尾“了解更多”
關鍵點, column-count: 元素內容將被劃分的最佳列數 break-inside: 避免在元素內部插入分頁符
// pug 模板引擎 div.g-container -for(var j=0; j<32; j++) div.g-item
column-count 看起來比display: flex更科學,模板不需要2層循環,更簡潔明了。如果真正用到數據上面,會更好處理。
$count: 32; // 隨機數(瀑布流某塊的高度) @function randomNum($max, $min: 0, $u: 1) { @return ($min + random($max)) * $u; } // 隨機顏色值 @function randomColor() { @return rgb(randomNum(255), randomNum(255), randomNum(255)); } .g-container { column-count: 4; column-gap: .5vw; padding-top: .5vw; } .g-item { position: relative; width: 24vw; margin-bottom: 1vw; break-inside: avoid; } @for $i from 1 to $count+1 { .g-item:nth-child(#{$i}) { height: #{randomNum(300, 50)}px; background: randomColor(); &::after { content: "#{$i}"; position: absolute; color: #fff; font-size: 2vw; top: 50%; left: 50%; transform: translate(-50%, -50%); } } }
預覽:
CSS 實現瀑布流布局(column-count)
演示地址: 點擊文章結尾“了解更多”
關鍵點, 使用 grid-template-columns、grid-template-rows 分割行列 使用 grid-row 控制每個 item 的所占格子的大小
// pug 模板引擎 div.g-container -for(var i=0; i<8; i++) div.g-item
樣式
$count: 8; // 隨機數(瀑布流某塊的高度) @function randomNum($max, $min: 0, $u: 1) { @return ($min + random($max)) * $u; } // 隨機顏色值 @function randomColor() { @return rgb(randomNum(255), randomNum(255), randomNum(255)); } .g-container { height: 100vh; display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(8, 1fr); } @for $i from 1 to $count+1 { .g-item:nth-child(#{$i}) { position: relative; background: randomColor(); margin: 0.5vw; &::after { content: "#{$i}"; position: absolute; color: #fff; font-size: 2vw; top: 50%; left: 50%; transform: translate(-50%, -50%); } } } .g-item { &:nth-child(1) { grid-column: 1; grid-row: 1 / 3; } &:nth-child(2) { grid-column: 2; grid-row: 1 / 4; } &:nth-child(3) { grid-column: 3; grid-row: 1 / 5; } &:nth-child(4) { grid-column: 4; grid-row: 1 / 6; } &:nth-child(5) { grid-column: 1; grid-row: 3 / 9; } &:nth-child(6) { grid-column: 2; grid-row: 4 / 9; } &:nth-child(7) { grid-column: 3; grid-row: 5 / 9; } &:nth-child(8) { grid-column: 4; grid-row: 6 / 9; } }
display: grid樣式上面感覺也不好用,需要書寫很多grid-column、grid-row。
預覽:
CSS 實現瀑布流布局(display: grid)
演示地址: 點擊文章結尾“了解更多”
通過,這3種CSS瀑布流布局,你更喜歡哪一種呢?
個人更喜歡column-count,看起來更加清晰,容易理解,代碼量也很少。
喜歡小編的點擊關注,了解更多知識!
源碼地址請點擊下方“了解更多”
*請認真填寫需求信息,我們會在24小時內與您取得聯系。