JS通過canvas api操作canvas元素實現圖形繪制與動畫效果,首先獲取上下文并調用api繪圖,結合requestanimationframe創建動畫。1.定義canvas元素并指定id和尺寸;2.使用js獲取canvas元素及其2d渲染上下文,若失敗則提示錯誤;3.使用ctx對象繪制矩形、圓形、線條等圖形;4.優化性能時減少重繪區域,使用離屏canvas及requestanimationframe;5.canvas動畫中利用requestanimationframe控制幀率,避免卡頓;6.監聽點擊事件需計算坐標是否在圖形內,或使用ispointinpath方法;7.文本換行需手動計算分行繪制,樣式通過font屬性控制;8.根據需求選擇canvas或webgl,前者適合簡單2d圖形,后者適合高性能3d圖形。
簡而言之,html 中,JS 通過 Canvas API 操縱
解決方案
首先,在 HTML 中定義一個
<canvas id="myCanvas" width="500" style="max-width:90%"></canvas>
接下來,使用 JavaScript 獲取 canvas 元素,并獲取其 2D 渲染上下文:
立即學習“前端免費學習筆記(深入)”;
const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); if (ctx) { // 繪圖操作在這里進行 } else { console.error('Canvas 上下文獲取失敗,瀏覽器不支持 Canvas 或配置錯誤。'); }
如果 getContext(‘2d’) 返回 NULL,很可能是因為瀏覽器版本過低,或者 canvas 元素本身存在問題。務必檢查 HTML 結構和瀏覽器兼容性。
現在,我們可以使用 ctx 對象提供的各種繪圖 API。例如,繪制一個矩形:
ctx.fillStyle = 'red'; // 設置填充顏色 ctx.fillRect(50, 50, 100, 80); // 繪制矩形,參數:x, y, width, height
繪制圓形:
ctx.beginPath(); // 開始一個新的路徑 ctx.arc(200, 150, 40, 0, 2 * Math.PI); // 圓心坐標,半徑,起始角度,結束角度 ctx.fillStyle = 'blue'; ctx.fill(); // 填充圓形 ctx.closePath(); // 關閉路徑
繪制線條:
ctx.beginPath(); ctx.moveTo(300, 50); // 起始點 ctx.lineTo(400, 150); // 終點 ctx.strokeStyle = 'green'; // 設置線條顏色 ctx.lineWidth = 5; // 設置線條寬度 ctx.stroke(); // 繪制線條 ctx.closePath();
這些只是 Canvas API 的冰山一角。 還有 strokeRect、clearRect、fillText、drawImage 等等,功能非常強大。
Canvas 性能優化:如何避免卡頓?
Canvas 性能優化是一個老生常談的問題。頻繁的重繪,特別是復雜圖形,很容易導致卡頓。
-
減少重繪區域: 只更新需要改變的部分。使用 clearRect 清除特定區域,而不是整個畫布。
-
離屏 Canvas: 先在內存中的 Canvas 上繪制,然后一次性將結果繪制到屏幕 Canvas 上。這可以減少屏幕重繪次數。
const offscreenCanvas = document.createElement('canvas'); offscreenCanvas.width = canvas.width; offscreenCanvas.height = canvas.height; const offscreenCtx = offscreenCanvas.getContext('2d'); // 在 offscreenCanvas 上繪制復雜圖形 offscreenCtx.fillStyle = 'purple'; offscreenCtx.fillRect(0, 0, 100, 100); // 一次性繪制到屏幕 Canvas ctx.drawImage(offscreenCanvas, 0, 0);
-
使用 requestAnimationFrame: 這是瀏覽器提供的專門用于動畫的 API,可以確保動畫在最佳時機執行,避免不必要的重繪。
function animate() { // 更新動畫狀態 // ... // 繪制 ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空畫布 // ... 繪制新的圖形 ... requestAnimationFrame(animate); } requestAnimationFrame(animate); // 啟動動畫
-
避免在繪制循環中創建對象: 這會頻繁觸發垃圾回收,影響性能。預先創建好對象,然后在循環中復用。
-
考慮 WebGL: 如果 Canvas 繪制過于復雜,可以考慮使用 WebGL,它利用 GPU 進行渲染,性能更高。不過,WebGL 的學習曲線也更陡峭。
Canvas 動畫:requestAnimationFrame 的正確使用姿勢
requestAnimationFrame 是實現 Canvas 動畫的關鍵。它告訴瀏覽器希望執行一個動畫,并請求瀏覽器在下一次重繪之前調用指定的回調函數。
let startTime = null; function animate(currentTime) { if (!startTime) startTime = currentTime; const progress = currentTime - startTime; // 根據時間進度更新動畫狀態 const x = Math.sin(progress / 1000) * 100 + 200; // 示例:正弦運動 // 繪制 ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.beginPath(); ctx.arc(x, 150, 20, 0, 2 * Math.PI); ctx.fillStyle = 'orange'; ctx.fill(); ctx.closePath(); requestAnimationFrame(animate); } requestAnimationFrame(animate);
這里需要注意幾點:
- requestAnimationFrame 的回調函數接收一個參數 currentTime,表示當前時間。可以用它來計算動畫的進度。
- 動畫的邏輯應該放在 animate 函數中。
- 每次繪制前,通常需要使用 clearRect 清空畫布。
- 必須再次調用 requestAnimationFrame 來安排下一次動畫幀。
Canvas 事件處理:如何監聽點擊事件?
Canvas 本身不具備 dom 元素那樣的事件監聽能力。 需要自己計算鼠標點擊的位置是否在某個圖形內部。
canvas.addEventListener('click', function(event) { const rect = canvas.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; // 檢查是否點擊了某個圓形 const circleX = 200; const circleY = 150; const circleRadius = 40; const distance = Math.sqrt((x - circleX) * (x - circleX) + (y - circleY) * (y - circleY)); if (distance <= circleRadius) { alert('點擊了圓形!'); } });
這段代碼監聽了 canvas 的 click 事件,計算了鼠標點擊的坐標,然后判斷該坐標是否在某個圓形的內部。
這種方法適用于簡單的圖形。 對于復雜的圖形,可以使用 Canvas 的 isPointInPath() 方法:
ctx.beginPath(); ctx.rect(50, 50, 100, 80); // 定義一個矩形 ctx.closePath(); if (ctx.isPointInPath(x, y)) { alert('點擊了矩形!'); }
isPointInPath() 方法會檢查指定的坐標是否在當前路徑的內部。 使用前,需要先定義好路徑。
Canvas 文本渲染:如何實現換行和樣式控制?
Canvas 文本渲染相對簡單,但要實現復雜的樣式控制和換行,需要一些技巧。
基本的文本渲染:
ctx.font = '20px Arial'; // 設置字體 ctx.fillStyle = 'black'; // 設置顏色 ctx.fillText('Hello, Canvas!', 50, 50); // 繪制文本,參數:文本內容,x, y
實現換行:
Canvas 本身沒有提供自動換行的功能。需要手動計算文本的寬度,然后分行繪制。
const text = 'This is a long text that needs to be wrapped to multiple lines.'; const maxWidth = 200; // 最大寬度 const lineHeight = 25; // 行高 let x = 50; let y = 50; function wrapText(context, text, x, y, maxWidth, lineHeight) { const words = text.split(' '); let line = ''; for (let n = 0; n < words.length; n++) { const testLine = line + words[n] + ' '; const metrics = context.measureText(testLine); const testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { context.fillText(line, x, y); line = words[n] + ' '; y += lineHeight; } else { line = testLine; } } context.fillText(line, x, y); } wrapText(ctx, text, x, y, maxWidth, lineHeight);
這個 wrapText 函數會將文本按照指定的寬度進行換行。
樣式控制:
Canvas 文本樣式控制主要通過 font 屬性實現。 可以設置字體大小、字體類型、字體粗細等。
ctx.font = 'bold 30px "Times New Roman", serif'; ctx.fillStyle = 'red'; ctx.fillText('Styled Text', 50, 100);
Canvas 的文本渲染能力相對有限,如果需要更復雜的文本排版,可以考慮使用 SVG 或 HTML。
Canvas 與 WebGL:如何選擇?
Canvas 和 WebGL 都是用于在瀏覽器中繪制圖形的技術,但它們的應用場景和性能特點有所不同。
-
Canvas: 基于像素的繪圖,使用 JavaScript API 進行操作。 適合繪制簡單的 2D 圖形、圖表、動畫等。 學習曲線相對平緩。
-
WebGL: 基于向量的繪圖,利用 GPU 進行加速。 適合繪制復雜的 3D 圖形、游戲、可視化等。 性能更高,但學習曲線陡峭。
選擇哪種技術取決于具體的需求。 如果只需要繪制簡單的 2D 圖形,Canvas 是一個不錯的選擇。 如果需要繪制復雜的 3D 圖形,或者對性能要求很高,WebGL 則是更好的選擇。
有時候,也可以將 Canvas 和 WebGL 結合使用。 例如,使用 WebGL 繪制 3D 圖形,然后將結果渲染到 Canvas 上,再在 Canvas 上添加一些 2D 元素。