HTML 中,通過 JavaScript 來獲取當前元素的高度通常使用以下屬性:
var element = document.getElementById("yourElementId"); // 獲取元素var height = element.offsetHeight; // 獲取元素高度(包括padding、border,但不包括margin)
如果你想獲取元素的 CSS 定義的高度(不包括 padding 和 border),可以使用 style.height
,但這只能獲取到直接寫在元素行內樣式中的高度,而不是計算后的實際高度或 CSS 樣式表中定義的高度:
var heightInStyle = element.style.height; // 只獲取行內樣式設置的高度
在 React 中獲取當前元素的高度方式與 JavaScript 相似,但是你需要確保在 DOM 更新后獲取元素高度。可以使用 ref
來訪問實際 DOM 節點并獲取其高度:
import React, { useRef, useEffect } from 'react';function YourComponent() { const elementRef = useRef(null); useEffect(() => { if (elementRef.current) { // 在這里,elementRef.current.clientHeight 獲取元素的內容區域高度(不包括padding和border) // elementRef.current.offsetHeight 獲取元素的實際渲染高度(包括padding和border,但不包括margin) console.log('Element height:', elementRef.current.offsetHeight); } }, []); // 確保此useEffect只在組件掛載后執行一次 return ( <div ref={elementRef}> {/* 你的組件內容 */} </div> ); }export default YourComponent;
在上述代碼中,useRef
創建了一個可變的引用對象,它可以用來保存任何可變值,包括 DOM 節點。然后通過將這個 ref 對象賦給元素的 ref
屬性,React 會將對應的 DOM 節點保存到這個 ref 對象的 .current
屬性上,這樣我們就可以在回調函數或者其他適當的地方訪問到該 DOM 節點,并獲取其高度了。
如果要在圖片加載完成后獲取包含圖片的元素高度,可以監聽圖片的 load
事件。在 React 中,你可以在組件內創建一個圖片引用,并在 useEffect
中監聽圖片加載完成:
import React, { useRef, useEffect } from 'react';function YourComponent() { const elementRef = useRef(null); const imgRef = useRef(null); useEffect(() => { const handleImageLoad = () => { if (elementRef.current) { console.log('Element height after image load:', elementRef.current.offsetHeight); } }; // 如果img已經存在于DOM中,則立即觸發handleImageLoad // 否則,在img加載完成后觸發handleImageLoad if (imgRef.current && imgRef.current.complete) { handleImageLoad(); } else { imgRef.current.onload = handleImageLoad; } // 可以選擇在組件卸載時清除事件監聽,避免內存泄漏 return () => { imgRef.current.onload = null; }; }, []); // 確保此useEffect只在組件掛載后執行一次 return ( <div ref={elementRef}> <img src="your-image-source.jpg" ref={imgRef} alt="Your Image" /> {/* 其他內容 */} </div> ); }export default YourComponent;
這樣,當圖片加載完成后,就會觸發 handleImageLoad
函數,從而獲取到包含圖片的元素的實際高度。
如果圖片是服務端渲染的,并且你無法直接在 img
標簽上添加 ref
,你可以考慮監聽整個組件的 onLoad
事件來判斷圖片是否加載完成。由于 React 在瀏覽器中重新渲染時會保留 DOM 節點(除非有更改),所以可以通過檢查元素的 offsetHeight
是否有變化來判斷圖片是否加載完畢。
、canvas簡介
? <canvas> 是 HTML5 新增的,一個可以使用腳本(通常為JavaScript)在其中繪制圖像的 HTML 元素。它可以用來制作照片集或者制作簡單(也不是那么簡單)的動畫,甚至可以進行實時視頻處理和渲染。
? 它最初由蘋果內部使用自己MacOS X WebKit推出,供應用程序使用像儀表盤的構件和 Safari 瀏覽器使用。 后來,有人通過Gecko內核的瀏覽器 (尤其是Mozilla和Firefox),Opera和Chrome和超文本網絡應用技術工作組建議為下一代的網絡技術使用該元素。
? Canvas是由HTML代碼配合高度和寬度屬性而定義出的可繪制區域。JavaScript代碼可以訪問該區域,類似于其他通用的二維API,通過一套完整的繪圖函數來動態生成圖形。
? Mozilla 程序從 Gecko 1.8 (Firefox 1.5)開始支持 <canvas>, Internet Explorer 從IE9開始<canvas> 。Chrome和Opera 9+ 也支持 <canvas>。
二、Canvas基本使用
2.1 <canvas>元素
<canvas id="tutorial" width="300" height="300"></canvas>
? <canvas>看起來和<img>標簽一樣,只是 <canvas> 只有兩個可選的屬性 width、heigth 屬性,而沒有 src、alt 屬性。
? 如果不給<canvas>設置widht、height屬性時,則默認 width為300、height為150,單位都是px。也可以使用css屬性來設置寬高,但是如寬高屬性和初始比例不一致,他會出現扭曲。所以,建議永遠不要使用css屬性來設置<canvas>的寬高。
###替換內容
? 由于某些較老的瀏覽器(尤其是IE9之前的IE瀏覽器)或者瀏覽器不支持HTML元素<canvas>,在這些瀏覽器上你應該總是能展示替代內容。
? 支持<canvas>的瀏覽器會只渲染<canvas>標簽,而忽略其中的替代內容。不支持 <canvas> 的瀏覽器則 會直接渲染替代內容。
用文本替換:
<canvas> 你的瀏覽器不支持canvas,請升級你的瀏覽器 </canvas>
用 <img> 替換:
<canvas> <img src="./美女.jpg" alt=""> </canvas>
結束標簽</canvas>不可省
與 <img>元素不同,<canvas>元素需要結束標簽(</canvas>)。如果結束標簽不存在,則文檔的其余部分會被認為是替代內容,將不會顯示出來。
2.2 渲染上下文(Thre Rending Context)
? <canvas>會創建一個固定大小的畫布,會公開一個或多個 渲染上下文(畫筆),使用 渲染上下文來繪制和處理要展示的內容。
? 我們重點研究 2D渲染上下文。 其他的上下文我們暫不研究,比如, WebGL使用了基于OpenGL ES的3D上下文 (“experimental-webgl”) 。
var canvas = document.getElementById('tutorial'); //獲得 2d 上下文對象 var ctx = canvas.getContext('2d');
2.3 檢測支持性
var canvas = document.getElementById('tutorial'); if (canvas.getContext){ var ctx = canvas.getContext('2d'); // drawing code here } else { // canvas-unsupported code here }
2.4 代碼模板
<html> <head> <title>Canvas tutorial</title> <style type="text/css"> canvas { border: 1px solid black; } </style> </head> <canvas id="tutorial" width="300" height="300"></canvas> </body> <script type="text/javascript"> function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); //開始代碼 } draw(); </script> </html>
2.5 一個簡單的例子
繪制兩個長方形。
<html> <head> <title>Canvas tutorial</title> <style type="text/css"> canvas { border: 1px solid black; } </style> </head> <canvas id="tutorial" width="300" height="300"></canvas> </body> <script type="text/javascript"> function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "rgb(200,0,0)"; //繪制矩形 ctx.fillRect (10, 10, 55, 50); ctx.fillStyle = "rgba(0, 0, 200, 0.5)"; ctx.fillRect (30, 30, 55, 50); } draw(); </script> </html>
三、繪制形狀
3.1 柵格(grid)和坐標空間
? 如下圖所示,canvas元素默認被網格所覆蓋。通常來說網格中的一個單元相當于canvas元素中的一像素。柵格的起點為左上角(坐標為(0,0))。所有元素的位置都相對于原點來定位。所以圖中藍色方形左上角的坐標為距離左邊(X軸)x像素,距離上邊(Y軸)y像素(坐標為(x,y))。
? 后面我們會涉及到坐標原點的平移、網格的旋轉以及縮放等。
3.2 繪制矩形
? <canvas> 只支持一種原生的 圖形繪制:矩形。所有其他圖形都至少需要生成一種路徑(path)。不過,我們擁有眾多路徑生成的方法讓復雜圖形的繪制成為了可能。
canvast 提供了三種方法繪制矩形:
fillRect(x, y, width, height)
繪制一個填充的矩形
strokeRect(x, y, width, height)
繪制一個矩形的邊框
clearRect(x, y, widh, height)
清除指定的矩形區域,然后這塊區域會變的完全透明。
說明:
? 這3個方法具有相同的參數。
? x, y:指的是矩形的左上角的坐標。(相對于canvas的坐標原點)
? width, height:指的是繪制的矩形的寬和高。
function draw(){ var canvas = document.getElementById('tutorial'); if(!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillRect(10, 10, 100, 50); //繪制矩形,填充的默認顏色為黑色 ctx.strokeRect(10, 70, 100, 50); //繪制矩形邊框 } draw(); ctx.clearRect(15, 15, 50, 25);
四、繪制路徑(path)
? 圖形的基本元素是路徑。
? 路徑是通過不同顏色和寬度的線段或曲線相連形成的不同形狀的點的集合。
? 一個路徑,甚至一個子路徑,都是閉合的。
使用路徑繪制圖形需要一些額外的步驟:
創建路徑起始點
調用繪制方法去繪制出路徑
把路徑封閉
一旦路徑生成,通過描邊或填充路徑區域來渲染圖形。
下面是需要用到的方法:
beginPath()
新建一條路徑,路徑一旦創建成功,圖形繪制命令被指向到路徑上生成路徑
moveTo(x, y)
把畫筆移動到指定的坐標(x, y)。相當于設置路徑的起始點坐標。
closePath()
閉合路徑之后,圖形繪制命令又重新指向到上下文中
stroke()
通過線條來繪制圖形輪廓
fill()
通過填充路徑的內容區域生成實心的圖形
4.1 繪制線段
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); //新建一條path ctx.moveTo(50, 50); //把畫筆移動到指定的坐標 ctx.lineTo(200, 50); //繪制一條從當前位置到指定坐標(200, 50)的直線. //閉合路徑。會拉一條從當前點到path起始點的直線。如果當前點與起始點重合,則什么都不做 ctx.closePath(); ctx.stroke(); //繪制路徑。 } draw();
4.2 繪制三角形邊框
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(200, 50); ctx.lineTo(200, 200); ctx.closePath(); //雖然我們只繪制了兩條線段,但是closePath會closePath,仍然是一個3角形 ctx.stroke(); //描邊。stroke不會自動closePath() } draw();
4.3 填充三角形
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); ctx.lineTo(200, 50); ctx.lineTo(200, 200); ctx.fill(); //填充閉合區域。如果path沒有閉合,則fill()會自動閉合路徑。 } draw();
4 繪制圓弧
有兩個方法可以繪制圓?。?/p>
arc(x, y, r, startAngle, endAngle, anticlockwise):
以(x, y)為圓心,以r為半徑,從 startAngle弧度開始到endAngle弧度結束。anticlosewise是布爾值,true表示逆時針,false表示順時針。(默認是順時針)
注意:
這里的度數都是弧度。
0弧度是指的x軸正方形
radians=(Math.PI/180)*degrees //角度轉換成弧度
arcTo(x1, y1, x2, y2, radius):
根據給定的控制點和半徑畫一段圓弧,最后再以直線連接兩個控制點。
圓弧案例1:
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(50, 50, 40, 0, Math.PI / 2, false); ctx.stroke(); } draw();
圓弧案例2:
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(50, 50, 40, 0, Math.PI / 2, false); ctx.stroke(); ctx.beginPath(); ctx.arc(150, 50, 40, 0, -Math.PI / 2, true); ctx.closePath(); ctx.stroke(); ctx.beginPath(); ctx.arc(50, 150, 40, -Math.PI / 2, Math.PI / 2, false); ctx.fill(); ctx.beginPath(); ctx.arc(150, 150, 40, 0, Math.PI, false); ctx.fill(); } draw();
圓弧案例3:
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(50, 50); //參數1、2:控制點1坐標 參數3、4:控制點2坐標 參數4:圓弧半徑 ctx.arcTo(200, 50, 200, 200, 100); ctx.lineTo(200, 200) ctx.stroke(); ctx.beginPath(); ctx.rect(50, 50, 10, 10); ctx.rect(200, 50, 10, 10) ctx.rect(200, 200, 10, 10) ctx.fill() } draw();
arcTo方法的說明:
? 這個方法可以這樣理解。繪制的弧形是由兩條切線所決定。
? 第 1 條切線:起始點和控制點1決定的直線。
? 第 2 條切線:控制點1 和控制點2決定的直線。
? 其實繪制的圓弧就是與這兩條直線相切的圓弧。
4.5 繪制貝塞爾曲線
4.5.1 什么是貝塞爾曲線
? 貝塞爾曲線(Bézier curve),又稱貝茲曲線或貝濟埃曲線,是應用于二維圖形應用程序的數學曲線。
? 一般的矢量圖形軟件通過它來精確畫出曲線,貝茲曲線由線段與節點組成,節點是可拖動的支點,線段像可伸縮的皮筋,我們在繪圖工具上看到的鋼筆工具就是來做這種矢量曲線的。
? 貝塞爾曲線是計算機圖形學中相當重要的參數曲線,在一些比較成熟的位圖軟件中也有貝塞爾曲線工具如PhotoShop等。在Flash4中還沒有完整的曲線工具,而在Flash5里面已經提供出貝塞爾曲線工具。
? 貝塞爾曲線于1962,由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所廣泛發表,他運用貝塞爾曲線來為汽車的主體進行設計。貝塞爾曲線最初由Paul de Casteljau于1959年運用de Casteljau演算法開發,以穩定數值的方法求出貝茲曲線。
一次貝塞爾曲線(線性貝塞爾曲線)
? 一次貝塞爾曲線其實是一條直線。
二次貝塞爾曲線
三次貝塞爾曲線
4.5.2 繪制貝塞爾曲線
繪制二次貝塞爾曲線
quadraticCurveTo(cp1x, cp1y, x, y):
說明:
? 參數1和2:控制點坐標
? 參數3和4:結束點坐標
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(10, 200); //起始點 var cp1x = 40, cp1y = 100; //控制點 var x = 200, y = 200; // 結束點 //繪制二次貝塞爾曲線 ctx.quadraticCurveTo(cp1x, cp1y, x, y); ctx.stroke(); ctx.beginPath(); ctx.rect(10, 200, 10, 10); ctx.rect(cp1x, cp1y, 10, 10); ctx.rect(x, y, 10, 10); ctx.fill(); } draw();
繪制三次貝塞爾曲線
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y):
說明:
? 參數1和2:控制點1的坐標
? 參數3和4:控制點2的坐標
? 參數5和6:結束點的坐標
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo(40, 200); //起始點 var cp1x = 20, cp1y = 100; //控制點1 var cp2x = 100, cp2y = 120; //控制點2 var x = 200, y = 200; // 結束點 //繪制二次貝塞爾曲線 ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); ctx.stroke(); ctx.beginPath(); ctx.rect(40, 200, 10, 10); ctx.rect(cp1x, cp1y, 10, 10); ctx.rect(cp2x, cp2y, 10, 10); ctx.rect(x, y, 10, 10); ctx.fill(); } draw();
五、添加樣式和顏色
? 在前面的繪制矩形章節中,只用到了默認的線條和顏色。
? 如果想要給圖形上色,有兩個重要的屬性可以做到。
fillStyle = color
設置圖形的填充顏色
strokeStyle = color
設置圖形輪廓的顏色
備注:
1. `color` 可以是表示 `css` 顏色值的字符串、漸變對象或者圖案對象。
2. 默認情況下,線條和填充顏色都是黑色。
3. 一旦您設置了 `strokeStyle` 或者 `fillStyle` 的值,那么這個新值就會成為新繪制的圖形的默認值。如果你要給每個圖形上不同的顏色,你需要重新設置 `fillStyle` 或 `strokeStyle` 的值。
1.fillStyle
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); for (var i = 0; i < 6; i++){ for (var j = 0; j < 6; j++){ ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' + Math.floor(255 - 42.5 * j) + ',0)'; ctx.fillRect(j * 50, i * 50, 50, 50); } } } draw();
2.strokeStyle
<script type="text/javascript"> function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); for (var i = 0; i < 6; i++){ for (var j = 0; j < 6; j++){ ctx.strokeStyle = `rgb(${randomInt(0, 255)},${randomInt(0, 255)},${randomInt(0, 255)})`; ctx.strokeRect(j * 50, i * 50, 40, 40); } } } draw();
/**
返回隨機的 [from, to] 之間的整數(包括from,也包括to)
*/
function randomInt(from, to){ return parseInt(Math.random() * (to - from + 1) + from); } </script>
3.Transparency(透明度)
globalAlpha = transparencyValue
? 這個屬性影響到 canvas 里所有圖形的透明度,有效的值范圍是 0.0 (完全透明)到 1.0(完全不透明),默認是 1.0。
? globalAlpha 屬性在需要繪制大量擁有相同透明度的圖形時候相當高效。不過,我認為使用rgba()設置透明度更加好一些。
line style
1. lineWidth = value
線寬。只能是正值。默認是1.0。
起始點和終點的連線為中心,上下各占線寬的一半
ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(100, 10); ctx.lineWidth = 10; ctx.stroke(); ctx.beginPath(); ctx.moveTo(110, 10); ctx.lineTo(160, 10) ctx.lineWidth = 20; ctx.stroke()
###2. lineCap = type
線條末端樣式。
共有3個值:
butt:線段末端以方形結束
round:線段末端以圓形結束
square:線段末端以方形結束,但是增加了一個寬度和線段相同,高度是線段厚度一半的矩形區域。
var lineCaps = ["butt", "round", "square"]; for (var i = 0; i < 3; i++){ ctx.beginPath(); ctx.moveTo(20 + 30 * i, 30); ctx.lineTo(20 + 30 * i, 100); ctx.lineWidth = 20; ctx.lineCap = lineCaps[i]; ctx.stroke(); } ctx.beginPath(); ctx.moveTo(0, 30); ctx.lineTo(300, 30); ctx.moveTo(0, 100); ctx.lineTo(300, 100) ctx.strokeStyle = "red"; ctx.lineWidth = 1; ctx.stroke();
3. lineJoin = type
同一個path內,設定線條與線條間接合處的樣式。
共有3個值round, bevel 和 miter:
round
通過填充一個額外的,圓心在相連部分末端的扇形,繪制拐角的形狀。 圓角的半徑是線段的寬度。
bevel
在相連部分的末端填充一個額外的以三角形為底的區域, 每個部分都有各自獨立的矩形拐角。
miter(默認)
通過延伸相連部分的外邊緣,使其相交于一點,形成一個額外的菱形區域。
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); var lineJoin = ['round', 'bevel', 'miter']; ctx.lineWidth = 20; for (var i = 0; i < lineJoin.length; i++){ ctx.lineJoin = lineJoin[i]; ctx.beginPath(); ctx.moveTo(50, 50 + i * 50); ctx.lineTo(100, 100 + i * 50); ctx.lineTo(150, 50 + i * 50); ctx.lineTo(200, 100 + i * 50); ctx.lineTo(250, 50 + i * 50); ctx.stroke(); } } draw();
4. 虛線
用 setLineDash 方法和 lineDashOffset 屬性來制定虛線樣式. setLineDash 方法接受一個數組,來指定線段與間隙的交替;lineDashOffset屬性設置起始偏移量.
function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.setLineDash([20, 5]); // [實線長度, 間隙長度] ctx.lineDashOffset = -0; ctx.strokeRect(50, 50, 210, 210); } draw();
備注:
? getLineDash():返回一個包含當前虛線樣式,長度為非負偶數的數組。
六、繪制文本
繪制文本的兩個方法
canvas 提供了兩種方法來渲染文本:
fillText(text, x, y [, maxWidth])
在指定的(x,y)位置填充指定的文本,繪制的最大寬度是可選的.
strokeText(text, x, y [, maxWidth])
在指定的(x,y)位置繪制文本邊框,繪制的最大寬度是可選的.
var ctx; function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; ctx = canvas.getContext("2d"); ctx.font = "100px sans-serif" ctx.fillText("天若有情", 10, 100); ctx.strokeText("天若有情", 10, 200) } draw();
給文本添加樣式
font = value
當前我們用來繪制文本的樣式。這個字符串使用和 CSS font屬性相同的語法. 默認的字體是 10px sans-serif。
textAlign = value
文本對齊選項. 可選的值包括:start, end, left, right or center. 默認值是 start。
textBaseline = value
基線對齊選項,可選的值包括:top, hanging, middle, alphabetic, ideographic, bottom。默認值是 alphabetic。
direction = value
文本方向??赡艿闹蛋ǎ簂tr, rtl, inherit。默認值是 inherit。
七、繪制圖片
? 我們也可以在canvas上直接繪制圖片。
7.1 由零開始創建圖片
創建<img>元素
var img = new Image(); // 創建一個<img>元素 img.src = 'myImage.png'; // 設置圖片源地址
腳本執行后圖片開始裝載
繪制img
//參數1:要繪制的img 參數2、3:繪制的img在canvas中的坐標
ctx.drawImage(img,0,0);
注意:
? 考慮到圖片是從網絡加載,如果 drawImage 的時候圖片還沒有完全加載完成,則什么都不做,個別瀏覽器會拋異常。所以我們應該保證在 img 繪制完成之后再 drawImage。
var img = new Image(); // 創建img元素 img.onload = function(){ ctx.drawImage(img, 0, 0) } img.src = 'myImage.png'; // 設置圖片源地址
7.2 繪制 img 標簽元素中的圖片
? img 可以 new 也可以來源于我們頁面的 <img>標簽
<img src="./美女.jpg" alt="" width="300"><br> <canvas id="tutorial" width="600" height="400"></canvas> <script type="text/javascript"> function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); var img = document.querySelector("img"); ctx.drawImage(img, 0, 0); } document.querySelector("img").onclick = function (){ draw(); } </script>
第一張圖片就是頁面中的<img>標簽
7.3 縮放圖片
drawImage() 也可以再添加兩個參數:
? drawImage(image, x, y, width, height)
? 這個方法多了2個參數:width 和 height,這兩個參數用來控制 當像canvas畫入時應該縮放的大小。
ctx.drawImage(img, 0, 0, 400, 200)
7.4 切片(slice)
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
? 第一個參數和其它的是相同的,都是一個圖像或者另一個 canvas 的引用。
其他8個參數:
? 前4個是定義圖像源的切片位置和大小,
? 后4個則是定義切片的目標顯示位置和大小。
八、狀態的保存和恢復
Saving and restoring state是繪制復雜圖形時必不可少的操作。
save()和restore()
? save 和 restore 方法是用來保存和恢復 canvas 狀態的,都沒有參數。
? Canvas 的狀態就是當前畫面應用的所有樣式和變形的一個快照。
關于 save()
Canvas狀態存儲在棧中,每當save()方法被調用后,當前的狀態就被推送到棧中保存。一個繪畫狀態包括:
當前應用的變形(即移動,旋轉和縮放)
strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation 的值
當前的裁切路徑(clipping path)
?
可以調用任意多次 save方法。(類似數組的push())
關于restore()
每一次調用 restore 方法,上一個保存的狀態就從棧中彈出,所有設定都恢復。(類似數組的pop())
var ctx; function draw(){ var canvas = document.getElementById('tutorial'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillRect(0, 0, 150, 150); // 使用默認設置繪制一個矩形 ctx.save(); // 保存默認狀態 ctx.fillStyle = 'red' // 在原有配置基礎上對顏色做改變 ctx.fillRect(15, 15, 120, 120); // 使用新的設置繪制一個矩形 ctx.save(); // 保存當前狀態 ctx.fillStyle = '#FFF' // 再次改變顏色配置 ctx.fillRect(30, 30, 90, 90); // 使用新的配置繪制一個矩形 ctx.restore(); // 重新加載之前的顏色狀態 ctx.fillRect(45, 45, 60, 60); // 使用上一次的配置繪制一個矩形 ctx.restore(); // 加載默認顏色配置 ctx.fillRect(60, 60, 30, 30); // 使用加載的配置繪制一個矩形 } draw();
九、變形
9.1 translate
translate(x, y)
? 用來移動 canvas 的原點到指定的位置
? translate方法接受兩個參數。x 是左右偏移量,y 是上下偏移量,如右圖所示。
在做變形之前先保存狀態是一個良好的習慣。大多數情況下,調用 restore 方法比手動恢復原先的狀態要簡單得多。又如果你是在一個循環中做位移但沒有保存和恢復canvas 的狀態,很可能到最后會發現怎么有些東西不見了,那是因為它很可能已經超出 canvas 范圍以外了。
? 注意:translate移動的是canvas的坐標原點。(坐標變換)
var ctx; function draw(){ var canvas = document.getElementById('tutorial1'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.save(); //保存坐原點平移之前的狀態 ctx.translate(100, 100); ctx.strokeRect(0, 0, 100, 100) ctx.restore(); //恢復到最初狀態 ctx.translate(220, 220); ctx.fillRect(0, 0, 100, 100) } draw();
9.2 rotate
rotate(angle)
? 旋轉坐標軸。
? 這個方法只接受一個參數:旋轉的角度(angle),它是順時針方向的,以弧度為單位的值。
? 旋轉的中心是坐標原點。
var ctx; function draw(){ var canvas = document.getElementById('tutorial1'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "red"; ctx.save(); ctx.translate(100, 100); ctx.rotate(Math.PI / 180 * 45); ctx.fillStyle = "blue"; ctx.fillRect(0, 0, 100, 100); ctx.restore(); ctx.save(); ctx.translate(0, 0); ctx.fillRect(0, 0, 50, 50) ctx.restore(); } draw();
9.3 scale
scale(x, y)
? 我們用它來增減圖形在 canvas 中的像素數目,對形狀,位圖進行縮小或者放大。
? scale方法接受兩個參數。x,y分別是橫軸和縱軸的縮放因子,它們都必須是正值。值比 1.0 小表示縮 小,比 1.0 大則表示放大,值為 1.0 時什么效果都沒有。
? 默認情況下,canvas 的 1 單位就是 1 個像素。舉例說,如果我們設置縮放因子是 0.5,1 個單位就變成對應 0.5 個像素,這樣繪制出來的形狀就會是原先的一半。同理,設置為 2.0 時,1 個單位就對應變成了 2 像素,繪制的結果就是圖形放大了 2 倍。
9.4 transform(變形矩陣)
transform(a, b, c, d, e, f)
a (m11)
? Horizontal scaling.
b (m12)
? Horizontal skewing.
c (m21)
? Vertical skewing.
d (m22)
? Vertical scaling.
e (dx)
? Horizontal moving.
f (dy)
? Vertical moving.
var ctx; function draw(){ var canvas = document.getElementById('tutorial1'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.transform(1, 1, 0, 1, 0, 0); ctx.fillRect(0, 0, 100, 100); } draw();
十、合成
? 在前面的所有例子中、,我們總是將一個圖形畫在另一個之上,對于其他更多的情況,僅僅這樣是遠遠不夠的。比如,對合成的圖形來說,繪制順序會有限制。不過,我們可以利用 globalCompositeOperation 屬性來改變這種狀況。
globalCompositeOperation = type
var ctx; function draw(){ var canvas = document.getElementById('tutorial1'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.fillStyle = "blue"; ctx.fillRect(0, 0, 200, 200); ctx.globalCompositeOperation = "source-over"; //全局合成操作 ctx.fillStyle = "red"; ctx.fillRect(100, 100, 200, 200); } draw();
注:下面的展示中,藍色是原有的,紅色是新的。
type `是下面 13 種字符串值之一:
##1. source-over(default)
這是默認設置,新圖像會覆蓋在原有圖像。
##2. source-in
僅僅會出現新圖像與原來圖像重疊的部分,其他區域都變成透明的。(包括其他的老圖像區域也會透明)
##3. source-out
僅僅顯示新圖像與老圖像沒有重疊的部分,其余部分全部透明。(老圖像也不顯示)
##4. source-atop
新圖像僅僅顯示與老圖像重疊區域。老圖像仍然可以顯示。
##5. destination-over
新圖像會在老圖像的下面。
##6. destination-in
僅僅新老圖像重疊部分的老圖像被顯示,其他區域全部透明。
##7. destination-out
僅僅老圖像與新圖像沒有重疊的部分。 注意顯示的是老圖像的部分區域。
##8. destination-atop
老圖像僅僅僅僅顯示重疊部分,新圖像會顯示在老圖像的下面。
##9. lighter
新老圖像都顯示,但是重疊區域的顏色做加處理
##10. darken
保留重疊部分最黑的像素。(每個顏色位進行比較,得到最小的)
blue: #0000ff
red: #ff0000
所以重疊部分的顏色:#000000
##11. lighten
保證重疊部分最量的像素。(每個顏色位進行比較,得到最大的)
blue: #0000ff
red: #ff0000
所以重疊部分的顏色:#ff00ff
##12. xor
重疊部分會變成透明
##13. copy
只有新圖像會被保留,其余的全部被清除(邊透明)
#十一、裁剪路徑
clip()
? 把已經創建的路徑轉換成裁剪路徑。
? 裁剪路徑的作用是遮罩。只顯示裁剪路徑內的區域,裁剪路徑外的區域會被隱藏。
? 注意:clip()只能遮罩在這個方法調用之后繪制的圖像,如果是clip()方法調用之前繪制的圖像,則無法實現遮罩。
var ctx; function draw(){ var canvas = document.getElementById('tutorial1'); if (!canvas.getContext) return; var ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.arc(20,20, 100, 0, Math.PI * 2); ctx.clip(); ctx.fillStyle = "pink"; ctx.fillRect(20, 20, 100,100); } draw();
十二、動畫
動畫的基本步驟
清空canvas
再繪制每一幀動畫之前,需要清空所有。清空所有最簡單的做法就是clearRect()方法
保存canvas狀態
如果在繪制的過程中會更改canvas的狀態(顏色、移動了坐標原點等),又在繪制每一幀時都是原始狀態的話,則最好保存下canvas的狀態
繪制動畫圖形
這一步才是真正的繪制動畫幀
恢復canvas狀態
如果你前面保存了canvas狀態,則應該在繪制完成一幀之后恢復canvas狀態。
控制動畫
我們可用通過canvas的方法或者自定義的方法把圖像會知道到canvas上。正常情況,我們能看到繪制的結果是在腳本執行結束之后。例如,我們不可能在一個 for 循環內部完成動畫。
也就是,為了執行動畫,我們需要一些可以定時執行重繪的方法。
一般用到下面三個方法:
setInterval()
setTimeout()
requestAnimationFrame()
##案例1:太陽系
let sun; let earth; let moon; let ctx; function init(){ sun = new Image(); earth = new Image(); moon = new Image(); sun.src = "sun.png"; earth.src = "earth.png"; moon.src = "moon.png"; let canvas = document.querySelector("#solar"); ctx = canvas.getContext("2d"); sun.onload = function (){ draw() } } init(); function draw(){ ctx.clearRect(0, 0, 300, 300); //清空所有的內容 /*繪制 太陽*/ ctx.drawImage(sun, 0, 0, 300, 300); ctx.save(); ctx.translate(150, 150); //繪制earth軌道 ctx.beginPath(); ctx.strokeStyle = "rgba(255,255,0,0.5)"; ctx.arc(0, 0, 100, 0, 2 * Math.PI) ctx.stroke() let time = new Date(); //繪制地球 ctx.rotate(2 * Math.PI / 60 * time.getSeconds() + 2 * Math.PI / 60000 * time.getMilliseconds()) ctx.translate(100, 0); ctx.drawImage(earth, -12, -12) //繪制月球軌道 ctx.beginPath(); ctx.strokeStyle = "rgba(255,255,255,.3)"; ctx.arc(0, 0, 40, 0, 2 * Math.PI); ctx.stroke(); //繪制月球 ctx.rotate(2 * Math.PI / 6 * time.getSeconds() + 2 * Math.PI / 6000 * time.getMilliseconds()); ctx.translate(40, 0); ctx.drawImage(moon, -3.5, -3.5); ctx.restore(); requestAnimationFrame(draw); }
##案例2:模擬時鐘
常,我們希望限制元素相對于其父元素的寬度,同時使其具有動態性。因此,有一個基礎寬度或高度的能力,使其擴展的基礎上,可用的空間。比如說,我們有一個按鈕,它的寬度應該是最小的,不應該低于它的寬度。這就是最大和最小屬性變得方便的地方。
在本文中,我們將詳細介紹CSS的最大和最小寬度和高度屬性,并使用可能的用例和技巧詳細解釋每一個屬性。
首先要討論的是與寬度相關的屬性。我們有min-width和max-width,它們中的每一個都很重要,都有自己的用例。
設置min-width的值時,其好處在于防止width屬性使用的值變得小于min-width的指定值。 請注意,min-width的默認值是auto,它解析為0。
讓我們舉一個基本的例子來說明這一點。
我們有一個按鈕,里面有一個變化的文本。文本的范圍可能從一個單詞到多個單詞。為了確保即使只有一個單詞,它也有最小寬度,應該使用min-width。
最小寬度為100px,這樣即使按鈕的內容很短,比如Done,或者只有一個圖標,它仍然足夠大,可以被注意到。在使用阿拉伯語等多語言網站時,這一點非常重要。 考慮以下來自Twitter的示例:
在以前的情況下,按鈕上帶有單詞“??”,表示完成。 按鈕的寬度太小,因此在后面的案例中,我增加了它的最小寬度。
在內容較長的情況下,min-width可以擴展按鈕的寬度,而水平方向上的padding應該被添加,以實現一個合適的外觀按鈕。
在設置max-width值時,它的好處在于防止width屬性使用的值超過max-width的指定值。max-width的默認值是none。
max-width的常見且簡單的用例是將其與圖像一起使用。 考慮以下示例:
圖像比它的父元素大。通過使用max-width: 100%,圖像的寬度不會超過其父圖像的寬度。如果圖像比父圖像小,則max-width: 100%不會對圖像產生實際影響,因為它比父圖像小。
當min-width和max-width都用于一個元素時,它們中的哪一個將覆蓋另一個?換句話說,哪個優先級更高?
html
<div class="wrapper">
<div class="sub"></div>
</div>
css
.sub {
width: 100px;
min-width: 50%;
max-width: 100%;
}
初始width值為100px,并在其上加上min-width和max-width值。 結果是元素寬度未超過其包含的塊/父元素的50%。
除了最小和最大寬度屬性外,我們還具有與高度相同的屬性。
設置min-height的值時,其好處在于防止使用的height屬性值變得小于min-height的指定值。 請注意,最小高度的默認值為auto,它解析為0。
我們用一個簡單的例子來演示一下。
我們有一個帶有描述文本的部分。目標是為section設置一個最小高度,這樣它就可以處理短或長內容??紤]下面的基本情況
.sub {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
min-height: 100px;
color: #fff;
background: #3c78dB;
}
最小高度為100px,使用flexbox時,內容水平和垂直居中。 如果內容更長,會發生什么? 例如一段?
是的,你猜對了!section的高度將展開以包含新內容。有了它,我們就可以構建靈活的組件,并對其內容做出響應。
事例源碼:https://codepen.io/shadeed/pen/cfb600cf30acdae9cf6f9cb5347a37cf
在設置max-height值時,它的好處在于防止height屬性使用的值超過max-height的指定值。注意,max-height的默認值是none。
考慮下面的示例,其中我為內容設置了max-height。 但是,因為它大于指定的空間,所以會發生溢出。 因此,文本超出了其父邊界。
我們將介紹min-width,min-height,max-width和max-height的一些常見和不常見的用例。
當有一個標簽列表時,建議限制一個標簽的最小寬度,這樣如果它的內容很短,它的外觀就不會受到影響。
通過具有這種靈活性,無論內容有多短,標簽都將看起來不錯。 但是,如果內容作者輸入了一個非常長的標簽名稱,而他使用的內容管理系統沒有標簽的最大字符長度,將會發生什么情況呢? 我們也可以使用max-width。
.c-tag {
display: inline-block;
min-width: 100px;
max-width: 250px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/*Other styles*/
}
通過使用max-width,標簽寬度將被限制為特定值。 但是,這還不夠,標簽名稱應被截斷。
事例地址:https://codepen.io/shadeed/pen/320e42b7ad75c438a9e633417d737d16
對于按鈕的最小值和最大值有不同的用例,因為按鈕組件有多種變體。考慮下面的圖:
請注意,按鈕的“Get”寬度太小。 如果不設置最小寬度,則由于任何原因而沒有文本時,情況可能會變得更糟。 在這種情況下,設置最小寬度很重要。
min-width的默認值是auto,它被計算為0。當一個元素是一個flex項時,min-width的值不會計算為零。flex 項目的最小大小等于其內容的大小。
根據CSSWG:
默認情況下,flex項目不會縮小到它們的最小內容大小(最長單詞或固定大小元素的長度)以下。要更改此設置,請設置min-width或 min-height屬性。
考慮下面的例子
這個人的名字有一個很長的單詞,這導致了溢出和水平滾動。盡管如此,我還是在標題中添加了下面的CSS來截斷它
.c-person__name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
由于title是flex項目,因此解決方案是重置min-width并將其強制為零。
.c-person__name {
/*Other styles*/
min-width: 0;
}
下面是修復后的樣子
根據CSSWG:
在彈性項目的主軸上可見溢出的項目上,當在彈性項目的主軸min-size屬性中指定時,指定自動最小尺寸。
意味著,將overflow設置為visible值以外的值會導致min-width被計算為0,這解決了我們不設置min-width: 0的問題。
事例源碼:https://codepen.io/shadeed/pen/398ccffcd437a2fb042f5ce3bdd68c57
雖然與min-width相比,這是一個不太常見的問題,但是它可能發生。 只是為了確認,問題與不能少于其內容的彈性項目有關。 結果min-height值被設置為與內容一樣長。
考慮以下示例:
用紅色表示的文本應該在父文本中裁剪。因為面板主體是一個flex項目,所以它的min-height與它的內容相等。為了防止這種情況,我們應該重新設置最小高度值。看看HTML和CSS是怎么樣的。
HTML
<div class="c-panel">
<h2 class="c-panel__title"><!-- Title --></h2>
<div class="c-panel__body">
<div class="c-panel__content"><!-- Content --></div>
</div>
</div>
CSS
.c-panel {
display: flex;
flex-direction: column;
height: 180px;
}
.c-panel__body {
min-height: 0;
}
.c-panel__content {
overflow-y: scroll;
height: 100%;
}
通過向面板主體添加min-height: 0,這將重置該屬性,并且現在應該可以正常工作。
事例源碼:https://codepen.io/shadeed/pen/dea75b84b1fcfd03e5c21173a40afc20?editors=0100
在某些情況下,我們有一個最小寬度的元素,但同時,它沒有最大寬度。這可能會導致組件太寬,而我們并不想這樣做??紤]以下示例
由于寬度是以像素為單位定義的,因此不能保證上面的方法適用于移動視口。為了解決這個問題,我們可以使用百分比來代替像素作為最小和最大屬性。考慮下面這個具有article主體的示例。
我為圖像添加了以下CSS:
img {
min-width: 35%;
max-width: 70%;
}
事例源碼:https://codepen.io/shadeed/pen/11f49fd1a35ad06ce241bee17c3d3124
#### 頁面包裝器/容器
最常用的`max-width`用例之一是頁面包裝器或容器。通過向頁面添加最大寬度,我們可以確保內容對用戶來說是可讀的、易于瀏覽的。
下面是一個包裝器的例子,它是居中的,左右兩邊有水平的填充。
.wrapper {
max-width: 1170px;
padding-left: 16px;
padding-right: 16px;
margin-left: auto;
margin-right: auto;
}
ch 是一個相對于數字0的大小,1ch 就是數字 0 的寬度。如定義一個3ch的寬度,那么就只能裝下 3個0。
<!-- HTML代碼 -->
<div>0000</div>
/* CSS代碼 */
div {
width: 3ch;
background: powderblue;
}
在前面的wrapper元素示例中,我們可以利用ch單元,因為它是一個article 主體。
.wrapper {
max-width: 70ch;
/* Other styles */
}
在某些情況下,我們面臨著使手風琴或移動菜單具有意想不到的內容高度的挑戰。在這種情況下,max-height可能是一個很好的解決方案。
請考慮以下示例:
單擊菜單按鈕后,菜單應隨動畫從上到下滑動。 如果沒有固定的高度(不建議這樣做),除非使用JavaScript,否則這是不可能的。 但是,對于max-height,這是可能的。 想法是為高度添加一個較大的值,例如max-height:20rem,可能無法達到,然后我們可以使用動畫從max-height: 0變換到max-height: 20rem。
.c-nav {
max-height: 0;
overflow: hidden;
transition: 0.3s linear;
}
.c-nav.is-active {
max-height: 22rem;
}
點擊菜單按鈕可以看到動畫的運行。
事例源碼:https://codepen.io/shadeed/pen/164c7ef67f5d4541bddb8bc12b2772da
一般來說,我不喜歡給元素添加固定的高度。我覺得這樣做,會破壞流式布局的結構。但有些情況設置固定高度卻很有用。
考慮下面的例子,在這里我們有一個設置了固定高度的hero部分。
是,當內容較長時,它會溢出并離開hero包裝器,這可不太好。
為了預先解決這個問題,我們可以使用min-height來代替height。我們可以用這種方式先解決問題,盡管這可能會導致頁面看起來很奇怪,但是我認為應該首先防止內容管理系統(CMS)中發生這樣的事情。這樣,問題就解決了,看起來也不錯。
內容溢出的問題不僅在于內容是否大于固定的hero 高度。它可以發生在屏幕大小調整作為文本換行的結果。
如果改用min-height,則上述情況根本不會發生。
對于模態組件,它需要最小和最大寬度,以便可以適應移動設備到PC的屏幕上的適應。
思路1
.c-modal__body {
width: 600px;
max-width: 100%;
}
思路2
.c-modal__body {
width: 100%;
max-width: 600px;
}
對于我來說,我更喜歡第二個思路,因為我只需要定義max-width: 600px。modal是一個<div>元素,因此它已經具有其父元素的100%寬度,對嗎?
考慮下面為模態設計簡化的測試案例。 請注意,如果可用視口空間不足,則寬度如何更改為其父級的100%。
事例源碼:https://codepen.io/shadeed/pen/5dcb1c4c6773cc3a97a766c327c36443
當一個網站的內容不夠長,它希望看到頁腳粘到底部。讓我們用一個可視化的例子來更好地展示這一點。
請注意,頁腳未粘貼在瀏覽器窗口的末尾。 那是因為內容不足以達到瀏覽器窗口高度的長度。 修復后,其外觀應如下所示:
首先,將body元素作為flexbox容器,然后將其最小高度設置為視口高度的100%。
事例源碼:https://codepen.io/shadeed/pen/aeb14f2819b9cc4805275b88c2d55645?editors=1100
為了使比例容器能夠根據視口大小進行響應縮放,引入了padding hack。 現在,我們可以通過組合CSS中的視口單位和最大寬度/高度來模仿相同的行為。
我們有一個尺寸為644 * 1000像素的圖像。 為了使其流暢,我們需要以下內容:
人才們的 【三連】 就是小智不斷分享的最大動力,如果本篇博客有任何錯誤和建議,歡迎人才們留言,最后,謝謝大家的觀看。
作者:Ahmad shaded 譯者:前端小智 來源:sitepoint 原文:https://www.impressivewebs.com/min-max-width-height-css
*請認真填寫需求信息,我們會在24小時內與您取得聯系。