HTML5 / Canvas có hỗ trợ đệm kép không?


83

Những gì tôi muốn làm là vẽ đồ họa của mình trên một bộ đệm và sau đó có thể sao chép nó vào canvas để tôi có thể tạo hoạt ảnh và tránh nhấp nháy. Nhưng tôi không thể tìm thấy tùy chọn này. Bất cứ ai biết làm thế nào tôi có thể đi về điều này?


12
Kinh nghiệm của tôi là bản vẽ canvas được liên kết bởi trình duyệt để hoạt ảnh mượt mà. Bạn có thể chia sẻ một số mã nhấp nháy như bạn mô tả?
mí mắt vào

2
Tôi đã nhận thấy IE có thể nhấp nháy trong một số trường hợp khi sử dụng explorercanvas, nhưng tất nhiên đó không phải là HTML5 và chỉ là một canvasphần tử đơn thuần được mô phỏng bởi VML. Tôi chưa bao giờ thấy bất kỳ trình duyệt nào khác làm điều đó.
cryo


2
Mã dành cho người mới bắt đầu thực sự ngu ngốc không nhấp nháy. jsfiddle.net/linuxlizard/ksohjr4f/3 Theo tất cả các quyền, sẽ nhấp nháy. Các trình duyệt rất ấn tượng.
David Poole

Bạn chỉ cần đệm kép nếu bạn có chức năng vẽ không đồng bộ. Miễn là bạn không nhượng bộ trình duyệt, tức là làm cho bản vẽ đồng bộ thì bạn sẽ ổn. Ngay sau khi bạn thêm một lời hứa hoặc setTimeout hoặc một cái gì đó vào đó, bạn trả lại trình duyệt và nó sẽ vẽ trạng thái hiện tại trước khi kết thúc hiệu quả gây nhấp nháy.
albertjan

Câu trả lời:


38

Liên kết hữu ích sau, ngoài việc hiển thị các ví dụ và lợi thế của việc sử dụng đệm kép, còn hiển thị một số mẹo hiệu suất khác để sử dụng phần tử canvas html5. Nó bao gồm các liên kết đến các bài kiểm tra jsPerf, tổng hợp các kết quả kiểm tra trên các trình duyệt vào một cơ sở dữ liệu Trình duyệt. Điều này đảm bảo rằng các mẹo hiệu suất đã được xác minh.

http://www.html5rocks.com/en/tutorials/canvas/performance/

Để thuận tiện cho bạn, tôi đã bao gồm một ví dụ tối thiểu về đệm kép hiệu quả như được mô tả trong bài viết.

// canvas element in DOM
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d');

// buffer canvas
var canvas2 = document.createElement('canvas');
canvas2.width = 150;
canvas2.height = 150;
var context2 = canvas2.getContext('2d');

// create something on the canvas
context2.beginPath();
context2.moveTo(10,10);
context2.lineTo(10,30);
context2.stroke();

//render the buffered canvas onto the original canvas element
context1.drawImage(canvas2, 0, 0);

83

Một phương pháp rất đơn giản là đặt hai phần tử canvas ở cùng một vị trí trên màn hình và đặt khả năng hiển thị cho vùng đệm mà bạn cần hiển thị. Vẽ trên ẩn và lật khi bạn hoàn thành.

Một số mã:

CSS:

canvas { border: 2px solid #000; position:absolute; top:0;left:0; 
visibility: hidden; }

Lật trong JS:

Buffers[1-DrawingBuffer].style.visibility='hidden';
Buffers[DrawingBuffer].style.visibility='visible';

DrawingBuffer=1-DrawingBuffer;

Trong đoạn mã này, mảng 'Buffers []' chứa cả hai đối tượng canvas. Vì vậy, khi bạn muốn bắt đầu vẽ, bạn vẫn cần lấy bối cảnh:

var context = Buffers[DrawingBuffer].getContext('2d');

Lạc đề: Cá nhân tôi thích sử dụng <noscript>và tạo các phần tử canvas của mình trong Javascript. Bạn thường muốn kiểm tra hỗ trợ canvas trong Javascript, vậy tại sao bạn lại muốn đặt thông báo canvas dự phòng trong HTML của mình?
Shea

19

Tất cả các trình duyệt mà tôi đã thử nghiệm đều xử lý bộ đệm này cho bạn bằng cách không sơn lại canvas cho đến khi mã vẽ khung của bạn hoàn tất. Xem thêm danh sách gửi thư WHWG: http://www.mail-archive.com/whatwg@lists.whatwg.org/msg19969.html


10
Tôi nhận thấy có hiện tượng nhấp nháy hoặc rách màn hình. Không chắc chắn làm thế nào để mô tả nó. Sử dụng Chrome mới nhất trên Linux.
grom

14

Bạn luôn có thể làm var canvas2 = document.createElement("canvas"); và hoàn toàn không thêm nó vào DOM.

Chỉ nói vì các bạn dường như quá ám ảnh với display:none; nó, đối với tôi dường như rõ ràng hơn và bắt chước ý tưởng về cách đệm kép chính xác hơn là chỉ có một khung vẽ vô hình một cách vụng về.


8

Hơn hai năm sau:

Không cần thực hiện đệm kép 'thủ công'. Ông Geary đã viết về điều này trong cuốn sách "HTML5 Canvas" .

Để giảm thiểu hiệu quả sử dụng nhấp nháy requestAnimationFrame()!


1
Bạn giải thích thế nào về những cải thiện hiệu suất đã được chứng kiến ​​bằng cách sử dụng đệm kép? html5rocks.com/en/tutorials/canvas/performance
Rick đề xuất

@ricksuggs Chà, "bộ đệm kép" hoặc "kết xuất ngoài màn hình" được đề cập trên html5rocks hơi khác một chút so với tôi nghĩ. Tôi đã coi DB là hoán đổi các trang màn hình (trong vram), mà hiệu quả chỉ là một hoạt động con trỏ thay vì sao chép các phần bộ nhớ. OP đã yêu cầu sử dụng DB để tránh nhấp nháy, điều này thực sự được giải quyết bởi requestAnimationFrame (). Có thể bài viết này về Kết xuất màn hình có thể thú vị. Ở đó, tôi trả lời câu hỏi của OP về việc sao chép và dán dữ liệu hình ảnh được đệm vào màn hình bằng Sprite Buffer
ohager

6

Josh đã hỏi (một lúc trở lại) về cách trình duyệt biết "khi nào quá trình vẽ kết thúc" để tránh nhấp nháy. Tôi đã bình luận trực tiếp vào bài đăng của anh ấy nhưng đại diện của tôi không đủ cao. Ngoài ra đây chỉ là ý kiến ​​của tôi. Tôi không có dữ kiện để sao lưu nó, nhưng tôi cảm thấy khá tự tin về nó và nó có thể hữu ích cho những người khác khi đọc nó trong tương lai.

Tôi đoán rằng trình duyệt không "biết" khi bạn vẽ xong. Nhưng cũng giống như hầu hết các javascript, miễn là mã của bạn chạy mà không chuyển giao quyền kiểm soát cho trình duyệt, trình duyệt về cơ bản bị khóa và sẽ không / không thể cập nhật / phản hồi giao diện người dùng của nó. Tôi đoán rằng nếu bạn xóa canvas và vẽ toàn bộ khung của mình mà không từ bỏ quyền kiểm soát cho trình duyệt, nó sẽ không thực sự vẽ canvas của bạn cho đến khi bạn hoàn thành.

Nếu bạn thiết lập một tình huống trong đó kết xuất của bạn kéo dài nhiều lệnh gọi setTimeout / setInterval / requestAnimationFrame, trong đó bạn xóa canvas trong một lần gọi và vẽ các phần tử trên canvas của mình trong một số lần gọi tiếp theo, lặp lại chu kỳ (ví dụ) sau mỗi 5 lần gọi, tôi sẵn sàng cá rằng bạn sẽ thấy nhấp nháy vì canvas sẽ được cập nhật sau mỗi cuộc gọi.

Điều đó nói rằng, tôi không chắc mình sẽ tin tưởng điều đó. Chúng tôi đã ở điểm mà javascript được biên dịch thành mã máy gốc trước khi thực thi (ít nhất đó là những gì động cơ V8 của Chrome thực hiện theo những gì tôi hiểu). Tôi sẽ không ngạc nhiên nếu không quá lâu trước khi các trình duyệt bắt đầu chạy javascript của họ trong một chuỗi riêng biệt từ giao diện người dùng và đồng bộ hóa bất kỳ quyền truy cập nào vào các phần tử giao diện người dùng cho phép giao diện người dùng cập nhật / phản hồi trong quá trình thực thi javascript không truy cập vào giao diện người dùng. Khi / nếu điều đó xảy ra (và tôi hiểu rằng có nhiều rào cản sẽ phải vượt qua, chẳng hạn như trình xử lý sự kiện khởi động trong khi bạn vẫn đang chạy mã khác), chúng ta có thể sẽ thấy hoạt ảnh nhấp nháy trên canvas không sử dụng một số loại đệm kép.

Cá nhân tôi thích ý tưởng về hai yếu tố canvas được đặt chồng lên nhau và xen kẽ được hiển thị / vẽ trên mỗi khung hình. Khá đơn giản và có thể khá dễ dàng được thêm vào một ứng dụng hiện có với một vài dòng mã.


Chính xác. Xem JSFiddle tại đây stackoverflow.com/questions/11777483/… để làm ví dụ.
Eric

6

Đối với những người không tin, đây là một số mã nhấp nháy. Lưu ý rằng tôi đang xóa rõ ràng để xóa vòng kết nối trước đó.

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

function draw_ball(ball) {
    ctx.clearRect(0, 0, 400, 400);
    ctx.fillStyle = "#FF0000";
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, 30, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fill();
}

var deltat = 1;
var ball = {};
ball.y = 0;
ball.x = 200;
ball.vy = 0;
ball.vx = 10;
ball.ay = 9.8;
ball.ax = 0.1;

function compute_position() {
    if (ball.y > 370 && ball.vy > 0) {
        ball.vy = -ball.vy * 84 / 86;
    }
    if (ball.x < 30) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    } else if (ball.x > 370) {
        ball.vx = -ball.vx;
        ball.ax = -ball.ax;
    }
    ball.ax = ball.ax / 2;
    ball.vx = ball.vx * 185 / 186;
    ball.y = ball.y + ball.vy * deltat + ball.ay * deltat * deltat / 2
    ball.x = ball.x + ball.vx * deltat + ball.ax * deltat * deltat / 2
    ball.vy = ball.vy + ball.ay * deltat
    ball.vx = ball.vx + ball.ax * deltat
    draw_ball(ball);
}

setInterval(compute_position, 40);
<!DOCTYPE html>
<html>
<head><title>Basketball</title></head>
<body>
<canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
</body></html>


4
Đối với tôi dường như không nhấp nháy, ít nhất là không khi quả bóng không di chuyển quá bán kính của nó mỗi khung hình. Chrome / Windows.
Jules,

10
Tôi đã thêm một lệnh gọi tới requestAnimFrame - xem tại đây - vào ví dụ của bạn tại jsfiddle.net/GzSWJ/28 - nó không nhấp nháy nữa
rhu 18/09/12

5

Không có hiện tượng nhấp nháy trong các trình duyệt web! Họ đã sử dụng bộ đệm dbl để kết xuất của họ. Công cụ Js sẽ thực hiện tất cả các kết xuất của bạn trước khi hiển thị nó. Ngoài ra, ngữ cảnh lưu và khôi phục chỉ dữ liệu ma trận chuyển đổi ngăn xếp và như vậy, chứ không phải chính nội dung canvas. Vì vậy, bạn không cần hoặc muốn đệm dbl!


8
Bạn có thể cung cấp một số bằng chứng để sao lưu các tuyên bố của mình không?
Rick đề nghị

@ricksuggs tôi tìm thấy không sử dụng kết quả DB trong jerkiness xấu trong phim hoạt hình, tôi havn't thử DB chưa
FutuToad

3

Thay vì tự mình lăn lộn, có lẽ bạn sẽ đạt được thành quả tốt nhất bằng cách sử dụng thư viện hiện có để tạo hoạt ảnh JavaScript rõ ràng và không nhấp nháy:

Đây là một cái phổ biến: http://processingjs.org


94
Chính xác! Tại sao phải bận tâm và viết 10 dòng mã của riêng bạn, khi bạn chỉ có thể sử dụng toàn bộ thư viện 275KB mà không có chút manh mối nào về nó !? Vâng, tôi đã bị mỉa mai.
Tom

10
Yeesh, khắc nghiệt nhiều không?
davidtbernal

3

bạn cần 2 canvas: (lưu ý chỉ số css z và vị trí: tuyệt đối)

<canvas id="layer1" width="760" height="600" style=" position:absolute; top:0;left:0; 
visibility: visible;  z-index: 0; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>
<canvas id="layer2" width="760" height="600" style="position:absolute; top:0;left:0; 
visibility: visible;  z-index: 1; solid #c3c3c3;">
Your browser does not support the canvas element.
</canvas>

bạn có thể nhận thấy rằng khung vẽ đầu tiên được hiển thị và bức vẽ thứ hai bị ẩn ý tưởng để vẽ trên phần ẩn sau đó chúng ta sẽ ẩn phần hiển thị và làm cho khung ẩn có thể nhìn thấy. khi nó ẩn 'clear canvas ẩn

<script type="text/javascript">
var buff=new Array(2);
buff[0]=document.getElementById("layer1");
buff[1]=document.getElementById("layer2");

ctx[0]=buff[0].getContext("2d");
ctx[1]=buff[1].getContext("2d");
var current=0;
// draw the canvas (ctx[ current ]);
buff[1- current ].style.visibility='hidden';
buff[ current ].style.visibility='visible';
ctx[1-current].clearRect(0,0,760,600);
current =1-current;

Thực hiện tốt, trong trường hợp ai đó cần áp dụng thủ công kỹ thuật đệm kép. Chỉ cần thêm dòng sau: var ctx = new Array (2); vào dòng trống trong mã của bạn ở trên.
dimmat

2

Opera 9.10 rất chậm và hiển thị quá trình vẽ. Nếu bạn muốn thấy một trình duyệt không sử dụng đệm kép, hãy dùng thử Opera 9.10.

Một số người cho rằng bằng cách nào đó trình duyệt đang xác định thời điểm quá trình vẽ kết thúc nhưng bạn có thể giải thích cách thức hoạt động không? Tôi không nhận thấy bất kỳ nhấp nháy rõ ràng nào trong Firefox, Chrome hoặc IE9 ngay cả khi bản vẽ chậm nên có vẻ như đó là những gì họ đang làm nhưng làm thế nào điều đó được thực hiện là một bí ẩn đối với tôi. Làm thế nào để trình duyệt biết rằng nó đang làm mới màn hình ngay trước khi các hướng dẫn vẽ khác được thực thi? Bạn có nghĩ rằng họ chỉ định thời gian để nếu một khoảng thời gian hơn 5ms hoặc lâu hơn trôi qua mà không thực hiện hướng dẫn vẽ canvas, nó giả định rằng nó có thể hoán đổi bộ đệm một cách an toàn?


2

Trong hầu hết các tình huống, bạn không cần phải làm điều này, trình duyệt sẽ thực hiện điều này cho bạn. Nhưng không phải lúc nào cũng hữu ích!

Bạn vẫn phải thực hiện điều này khi bản vẽ của bạn rất phức tạp. Hầu hết tốc độ cập nhật màn hình là khoảng 60Hz, nghĩa là màn hình cập nhật mỗi 16ms. Tỷ lệ cập nhật của trình duyệt có thể gần con số này. Nếu hình dạng của bạn cần 100ms để hoàn thành, bạn sẽ thấy một hình dạng chưa hoàn thành. Vì vậy, bạn có thể thực hiện đệm kép trong tình huống này.

Tôi đã thực hiện một bài kiểm tra: Clear a rect, wait for some time, then fill with some color.Nếu tôi đặt thời gian là 10ms, tôi sẽ không thấy nhấp nháy. Nhưng nếu tôi đặt nó thành 20ms, hiện tượng nhấp nháy sẽ xảy ra.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.