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
這個網站(minimal-portfolio-swart.vercel.app)發現一個不錯的交互效果,用戶體驗效果很不錯。如上圖所示,這個卡片上有一根白色的線條圍繞著卡片移動,且在線條的卡片內部跟隨這一塊模糊陰影,特別是在線條經過卡片圓角部分有特別絲滑的感覺。
今天的文章就來解析如何實現這種效果,文末附源碼預覽地址。根據示例圖片分析需要實現的功能點如下:
這里為什么單獨說明圓角部分是因為這塊需要特殊處理,請看后面的文章。
看到這個效果首先感覺是絲滑,沿著邊框移動的動畫元素如果是根據當前邊框實時計算而來的話,那么難度和算法會勸退很多人。
需要換一種思路,本質移動的線條元素和邊框并沒有關系,而是一個元素沿著邊框移動,線條和卡片內部的陰影就是一個元素,通過某種透視的方式產生了這種效果。
通過透視的方式實現一個邊框效果,我們可以用2個盒子嵌套,父級設置1像素的padding,如下代碼簡單的實現一個邊框效果。
.outer {
width: 400px;
height: 200px;
margin: 100px;
background: rgb(54, 224, 202);
padding: 1px;
position: relative;
}
.inner {
background: rgb(99, 99, 99);
width: 100%;
height: 100%;
}
效果圖:
然后增加一個子元素作為移動的元素,這個元素基于父級定位在邊框位置,由于動畫是沿著卡片內部四周移動,要確保在每一條邊上的透出的長度保持一致,所有創建的這個子元素是一個正方形。
.moving-element {
position: absolute;
top: 0;
left: 0;
width: 80px;
height: 80px;
background: #fff;
animation: moveAround 8s linear infinite;
}
并對這個元素增加簡單的animation動畫,沿著內邊框移動。
這個動畫需要注意的一個點是要使元素在移動的過程中保持勻速的動畫,需要計算每個關鍵幀之間的距離,并根據這些距離來調整每個關鍵幀的百分比。這樣可以確保元素在每個時間段內移動的距離與時間成正比,從而實現真正的勻速移動。
這里我們以上面的卡片舉例,其寬度為400px,高度為200px,元素沿矩形的邊框移動。
動畫代碼如下:
@keyframes moveAround {
0%, 100% {
top: 0px;
left: 0px;
}
33.33% {
top: 0px;
left: calc(100% - 80px);
}
50% {
top: calc(100% - 80px);
left: calc(100% - 80px);
}
83.33% {
top: calc(100% - 80px);
left: 0px;
}
}
最終完成的簡單版動畫效果如下:
這里為了方便大家看增加了透明度展示內部移動的元素,若去掉透明度則只有邊框上的一根線。
仔細看上面的圖可以發現在邊框盡頭時的過渡效果不好,瞬間從一條邊切換到另一條邊。首先還原網站的效果,增加邊框圓角,然后將內部移動的元素通過圓角變成一個圓形,這時候還需要同步調整內部元素的定位和動畫移動時設置的定位,保證內部圓形的中心和邊框的一致。
增加圓角處理:
.outer {
border-radius: 20px;
}
.inner {
border-radius: 20px;
}
.moving-element {
border-radius: 40px;
/* 圓心和邊框一致 */
transform: translate(-40px, -40px);
}
調整動畫過程中的定位:
@keyframes moveAround {
0%, 100% {
top: 0px;
left: 0px;
}
33.33% {
top: 0px;
left: 100%;
}
50% {
top: 100%;
left: 100%;
}
83.33% {
top: 100%;
left: 0px;
}
}
此時的動畫效果:
此時的邊框位置動畫已經很接近網站的效果,進一步觀察在圖中的效果可以發現在邊框角落的位置有一點卡頓的感覺,這是因為邊框位置我們設置了圓角,但是元素移動的軌跡是直角,導致視覺上停頓了一下。這里我們需要進一步優化animation。設置圓角后內部動畫元素移動的點應該從4個變成8個,且對應的位置需要和圓角的大小一一對應才能保障流暢的動畫效果。
如下所示黑色圓點是到四個頂點的動畫坐標,新的綠色圓點是基于圓角后的動畫移動坐標。
基于上面的動畫百分比算法計算出最新的比例及坐標代碼如下:
@keyframes moveAround {
0% { left: 40px; top: 0px; }
28.93% { left: 360px; top: 0px; }
33.99% { left: 400px; top: 40px; }
44.82% { left: 400px; top: 160px; }
49.88% { left: 360px; top: 200px; }
78.81% { left: 40px; top: 200px; }
83.87% { left: 0px; top: 160px; }
94.70% { left: 0px; top: 40px; }
100% { left: 40px; top: 0px; }
}
這里的動畫需要注意的是圓角部分綠色按鈕之間的動畫距離需要使用使用勾股定理計算。比如右上角的兩個點之間的計算方式是:
從 (360, 0) 到 (400, 40) = √((400-360)2 + (40-0)2) = √(1600 + 1600) = √3200 ≈ 56.57px
此時的動畫效果:
現在就差最后的陰影部分還未實現,仔細觀察移動的線條并不是全實心純色,而是有漸變的效果,目前移動的元素是一個正方形,設置背景色為徑向漸變即可,修改背景色的代碼如下:
background-image: radial-gradient(#fff 40%,transparent 80%);
現在還需要將內部的漸變進一步模糊,注意這里僅僅是模糊元素背后的背景,不能影響卡片上面其他的元素內容展示。這里我們使用backdrop-filter設置blur模糊效果。
CSS屬性 backdrop-filter 用于在元素后面的區域上應用圖形效果(如模糊或顏色偏移)。這個屬性可以讓你對元素背后的背景進行處理,而不影響元素本身的前景內容。
最后進一步調整顏色還原網站的效果如下:
這個效果不僅可以做卡片展示,作為按鈕的背景效果也很不錯:
到此整體的代碼實現過程就結束了,完整還原的網站的動畫效果。這是一個對用戶體驗很不錯的卡片效果,原網站實現的部分細節不一樣,整體實現原理差不多,基于兩個元素的1像素間距透出移動的線條,配合使用backdrop-filter設置純背景模糊效果,有興趣的可以嘗試看看。
作者:南城
來源-微信公眾號:南城大前端
出處:https://mp.weixin.qq.com/s/g-_3iD97PxmGL7RGwRrSvg
天我們一起來看幾個有趣的css動畫效果,用到的CSS3的知識點
線條圍繞著容器轉動
運行效果
首先,我們看下運行出來的效果圖
實現方式
這個效果并不是直接使用animation來實現的,而是通過clip屬性來實現的。外邊的藍色運動的線條實際為一個完整的div,只是通過clip屬性裁剪后只剩下上下左右之中的一邊
clip屬性依據上-右-下-左的順序,以左上角(0, 0)為標準點進行裁剪,如果傳入的參數為auto,則表示不裁剪
實例代碼
html部分的代碼
css部分的代碼
CSS部分代碼
CSS實現餅圖
在我們畫餅圖的時候會想到使用Canvas或者SVG,使用CSS我們同樣可以模擬出餅圖的形狀
運行效果
實現方式
首先要使用border-radius屬性將div設置為圓角
使用animation-delay屬性控制動畫效果
實例代碼
html部分代碼
css部分代碼
總結
今天這兩個有趣的CSS動畫效果,你學會了嗎?
括來說,用 HT for Web 做可視化主要分為兩部分,也就是 2D 和 3D。這兩部分需要單獨創建。在它們被創建完成后,我們再把它們集成到一起。
HT for Web 的 2D 部分主要是指 ht.graph.GraphView (簡稱 GraphView,也就是 2D 圖紙)。所謂 2D 圖紙其本質是一個 canvas。我們可以在上面進行基本圖形的繪制和編輯,進行連線布局,或者渲染動畫。GraphView 可以脫離開 3D 單獨使用,比如用于創建普通網頁,組態軟件,組織圖,流程圖等。
本節我們主要以一個示例說明一下 2D 圖紙的基本概念、功能及用法。其主要包括以下幾部分:
通過 new ht.graph.GraphView() 便可創建一張圖紙。創建完圖紙后,對于 HT 視圖組件,可以通過 GraphView.addToDOM() 方法將其添加到 DOM 中。addToDOM() 方法本身接收一個參數。如果為空,則默認添加到 body 下面。也可以通過傳遞一個 div 將 2D 圖紙固定到頁面的某個位置。
對于新創建的圖紙,其默認具有縮放、平移、編輯,以及框選等屬性。我們可以根據需要對其進行啟用或禁用。
與其他視圖組件一樣,如果不指定 DataModel, GraphView 自身也會創建一個空的 DataModel 容器用來管理所有添加到其里面的各種圖元。通過 GraphView.getDataModel() 或 GraphView.dm() 可以獲取該容器。對于 GraphView,它支持通過 DataModel.setBackground() 來配置圖紙的背景顏色。
/*************** 創建一張 2D 圖紙,添加到 body 下,并配置各種屬性 ******************/
const gv = new ht.graph.GraphView();
gv.addToDOM();
gv.setZoomable(true); // 可縮放,默認:true
gv.setPannable(true); // 可平移,默認:true
gv.setEditable(true); // 圖紙上的 Node 是否可編輯,默認:true
gv.setRectSelectable(true); // 是否允許對 Node 進行框選,默認:true
const dm = gv.getDataModel(); // 獲取圖紙的 DataModel,簡寫形式:gv.dm()
dm.setBackground('#DADADA'); // 同 dm.setBackground('rgba(218, 218, 218, 1)');
為什么要通過 DataModel 來設置圖紙的背景而不是直接操作 GraphView 本身?
在第一節我們提到過,為了能將我們創建的 2D/3D 數據保存與恢復,可以通過對 DataModel 進行序列化與反序列化來實現。注意,這里的序列化操作針對的是 DataModel 而不是視圖組件。因此,像如 GraphView 這種視圖組件,其背景顏色這種顯示屬性需要通過配置到 DataModel 才能保存下來。而像縮放,平移等操作屬性,則需要根據項目運行需要單獨配置。
有了 2D 圖紙的 DataModel,我們便可以向其添加節點或者叫圖元。這里我們添加了兩個機柜圖標。
/**************** 分別創建兩個 HT 節點并添加到圖紙中 ************************/
const server1 = new ht.Node();
server1.setSize(40, 100); // 節點寬高。應當根據圖片比例設置,不然會出現拉伸效果
server1.setPosition(100, 100); // 節點位置。左上角為(0,0)坐標
server1.setImage('assets/server.png'); // 節點圖片
server1.setName('Server 1'); // 顯示名稱
dm.add(server1); // 添加到 DataModel中,也就是添加到圖紙中
const server2 = new ht.Node();
server2.setSize(40, 100);
server2.setPosition(250, 100);
server2.setImage('assets/server.png');
server2.setName('Server 2');
dm.add(server2); // 注意一定要添加到 DataModel 中
在傳統前端(Vue, React, HTML)的開發過程中,要添加一個節點,我們往往需要先手動創建該節點(如添加一個 icon)并添加到 HTML 下面。然后通過數據綁定對該節點進行操作。而在 HT 中,我們只需要將新增的節點添加到 DataModel 下面。HT 的視圖組件會監聽 DataModel 的變化,自動觸發重新渲染操作。我們對節點的所有樣式風格配置和操作都可以直接在節點上進行。
ht.Node(簡稱為 Node)是 2D 圖紙和 3D 場景呈現節點圖元的基礎類,它繼承自 ht.Data。在 ht.Data 的基礎上,其又新增了位置,大小,旋轉,縮放,吸附等屬性。這些屬性都可以通過 set*/get* 來設置和獲取。
在 GraphView 中,新增一個 ht.Node 節點,默認其會顯示一個電腦圖片。我們可以通過 setImage(image) 方法來更改。如在上例的代碼中,我們找了一個機柜照片并將它給了新增的節點。
由于機柜圖片可能很大,我們可以通過 setSize(width, height) 來對節點大小進行控制。通過配置不同的寬高還可以實現拉伸效果。如果不想要拉伸效果,則可以根據原始圖片寬高比來設置節點的大小。
添加的節點如果不指定位置,其默認會被放到(x: 0, y: 0)點。通過 Node.setPosition(x, y) 或 Node.p(x, y) 可以控制節點的位置。通過 Node.getPosition() 或 Node.p() 可以獲取其坐標。在 GraphView 中,如果不手動修改節點的坐標,其在 GraphView 中的位置便是固定不變的。
由于 GraphView 本身具有縮放、平移等屬性,因此其里面的節點顯示在瀏覽器上的位置是不固定的。因此我們可以知道 GraphView 的坐標系與瀏覽器的坐標系是不一樣的。
實際上,GraphView 采用的是一個相對坐標系,其方向與 canvas 一致,即左上角為(0, 0)點。往右為 x 軸的正方向,往下為 y 軸的正方向。通過以下兩個方法可以在 GraphView 坐標與瀏覽器坐標之間進行轉換。
在添加了兩個機柜圖標之后,我們現在用連線將他們連接起來。HT 中的連線是用 ht.Edge 實現的。
ht.Edge 類型(簡稱 Edge)用于連接起始和目標兩個 Node 節點,兩個節點間可以有多條 Edge 存在,也允許起始和目標為同一節點。 可通過 new ht.Edge(source, target) 直接在構造函數中傳入 source 和 target 節點對象,也可構建 Edge 對象之后再分別設置。
/****************************** 創建連線 *************************************/
const edges = [];
// 創建三條連線連接 server1 和 server2
edges.push(createEdge(4, 'green', 6, 'yellow', [20, 10]));
edges.push(createEdge(3, '#fff', 3, '#000', [10, 10])); // 黑白線
edges.push(createEdge(10, 'rgb(51,153,255)', 5, 'rgb(242,83,75)', [5, 10])); // 紅藍線
function createEdge(width, color, dashWidth, dashColor, pattern) {
const edge = new ht.Edge(server1, server2);
edge.s({
'edge.width': width, // 連線寬度
'edge.gap': 30, // 連線與連線的距離
'edge.color': color, // 連線顏色。也可使用 rgb 或16進制顏色
'edge.dash': true, // 是否使用虛線
'edge.dash.width': dashWidth, // 虛線寬度
'edge.dash.color': dashColor, // 虛線顏色
'edge.dash.pattern': pattern, // 虛線與連線的占比。[虛線, 連線]
'edge.offset': 0, // 偏移
});
dm.add(edge); // 注意一定要添加到 DataModel 中
return edge;
}
// 創建第四條連線用于 server1 自連線
const edge = new ht.Edge();
edge.setSource(server1);
edge.setTarget(server1);
edge.s({
'edge.width': 5, // 連線寬度
'edge.gap': 30, // 連線與連線的距離
'edge.color': 'pink', // 連線顏色。也可使用 rgb 或16進制顏色
'edge.dash': true, // 是否使用虛線
'edge.dash.width': 5, // 虛線寬度
'edge.dash.color': 'purple', // 虛線顏色
'edge.dash.pattern': [10, 10], // 虛線與連線的占比。[虛線, 連線]
'edge.offset': 0, // 偏移
});
dm.add(edge); // 注意一定要添加到 DataModel 中
edges.push(edge);
上面的代碼創建了4條 Edge,其中前三條分別是連接 server1 和 server2,第四條是 server1 自連接。ht.Edge 的屬性主要是由 edge.s() 方法來配置,該方法是 edge.setStyle() 的簡寫形式。edge.s() 主要用來配置節點內置的屬性。HT for Web 引擎會根據屬性鍵值來渲染不同的效果。如果想要設置自定義屬性,需要使用 edge.a() 來實現。
與 ht.Node 相同的是,ht.Edge 也是由 ht.Data 擴展而來。但在大部分情況下,其又有自己的特點:
需要設置起始節點和目標節點。二者可以為同一個節點(如上例最后一條 Edge)。
缺少起始或目標節點的 Edge 不會在圖紙上顯示。
Edge 會跟隨節點移動。也就是說當我們拖動起始節點和目標節點的時候,其所相關的 Edge 會跟隨移動。
由于 Edge 的位置由兩個端點決定,因此 Edge 不支持 getPosition()/setPosition() 方法。同樣的,其寬度是在 edge.s() 中配置的,因此 Edge 也不支持 getSize()/setSize() 方法。
除了上面示例中的配置,ht.Edge 還支持自定義連線類型。如果嫌麻煩,也可以使用 HT 內置的十幾種連線類型,如 edge.s(’edge.type’, ‘boundary’) 就代表連線僅連接到圖元矩形邊緣。如果要使用內置連線類型,需要引入連線類型插件:
<script src="../../lib/plugin/ht-edgetype.js"></script>
在創建了連線后,如何讓它們流動起來呢?這里就用到了動畫功能。
要實現動畫功能,不外乎有這樣幾個關鍵屬性:動畫播放時長,播放過程中的屬性變化,播放完的回調事件。
HT 支持多種方式來實現動畫。這里我們選擇一種比較常用的。先來看代碼:
/****************************** 連線動畫 *************************************/
const animParams = {
// frames: 12, // 動畫幀數
// interval: 10, // 動畫幀間隔毫秒數
duration: 2000, // 動畫播放時長
easing: function(t){ return t * t; }, // 動畫緩動函數,默認采用`ht.Default.animEasing`
finishFunc: function(){
ht.Default.startAnim(animParams);
}, // 動畫結束后調用的函數。
action: function(v, t){ // action函數必須提供,實現動畫過程中的屬性變化。
// v代表通過easing(t)函數運算后的值,t代表當前動畫進行的進度,范圍:[0~1]
edges.forEach((edge, index) => {
const direction = index%2 == 0 ? 1 : -1;
edge.s('edge.dash.offset', t * 20 * direction);
});
}
};
ht.Default.startAnim(animParams);
這里 HT 用于播放動畫的方法是 ht.Default.startAnim(animParams),該方法會返回一個 anim 對象,可調用anim.stop(true) 終止動畫。同時 anim 還具有 anim.pause() 和 anim.resume() 方法可用來中斷和繼續動畫功能, 以及 anim.isRunning() 函數判斷動畫是否正在進行。
該方法所使用的參數也很簡單:
本節我們主要介紹了 HT for Web 圖紙的創建與基本配置。在 GraphView 中,我們可以向其 DataModel 添加 ht.Node 來繪制機柜。通過 Node.setPosition(x, y) 或 Node.p(x, y) 方法可以控制節點的位置,并通過 Node.getPosition() 或 Node.p() 方法來獲取其坐標。同時,GraphView 中的坐標系與瀏覽器坐標系不同,我們可以使用 GraphView.getLogicalPoint(event) 和 GraphView.getScreenPoint(point, y) 方法在兩種坐標系之間進行轉換。
對于連線,我們使用 ht.Edge 類型表示。創建 ht.Edge 對象時需要傳入起始節點和目標節點,同時可以通過 edge.s() 方法配置其屬性,如寬度、顏色、虛線等。在創建多條連線后,我們可以使用 ht.Default.startAnim(animParams) 方法來實現連線動畫,其中 animParams 是一個對象,包含動畫播放時長、緩動函數、動畫結束回調函數和動作控制函數等參數。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。