Каков наилучший способ установить один пиксель на холсте HTML5? - PullRequest
168 голосов
/ 04 февраля 2011

В HTML5 Canvas нет метода явной установки одного пикселя.

Возможно, можно установить пиксель, используя очень короткую линию, но тогда могут помешать сглаживание и ограничение строки.

Другим способом может быть создание небольшого ImageData объекта и использование:

context.putImageData(data, x, y)

чтобы поставить его на место.

Кто-нибудь может описать эффективный и надежный способ сделать это?

Ответы [ 14 ]

0 голосов
/ 01 декабря 2018

HANDY и предложение функции сложенного пикселя (pp) (ES6) (чтение-пиксель здесь ):

let pp= ((s='.myCanvas',c=document.querySelector(s),ctx=c.getContext('2d'),id=ctx.createImageData(1,1)) => (x,y,r=0,g=0,b=0,a=255)=>(id.data.set([r,g,b,a]),ctx.putImageData(id, x, y),c))()

pp(10,30,0,0,255,255);    // x,y,r,g,b,a ; return canvas object

let pp= ((s='.myCanvas',c=document.querySelector(s),ctx=c.getContext('2d'),id=ctx.createImageData(1,1)) => (x,y,r=0,g=0,b=0,a=255)=>(id.data.set([r,g,b,a]),ctx.putImageData(id, x, y),c))()

// draw shape
for(let i=0; i<800; i++) {
   let a = Math.PI*4*i/800;
   let x=16*Math.sin(a)**3;
   let y=13*Math.cos(a)-5*Math.cos(2*a)-2*Math.cos(3*a)-Math.cos(4*a);   
   pp(100+2.5*x,45-2.5*y,255,0,0,255)   
}
<canvas class="myCanvas" width=200 height=100 style="background: black"></canvas>

Эта функция использует putImageData и имеет часть инициализации (первая длинная строка).В начале вместо этого s='.myCanvas' используйте свой CSS-селектор для вашего холста.

Если вы хотите нормализовать параметры до значения от 0 до 1, вам следует изменить значение по умолчанию a=255 на a=1 и добавить строку: *От 1020 * до id.data.set([r*255,g*255,b*255,a*255]),ctx.putImageData(id, x*c.width, y*c.height)

Приведенный выше удобный код хорош для специальных графических алгоритмов тестирования или для проверки концепции, но он не подходит для использования в производстве, где код должен быть читаемым и понятным.

0 голосов
/ 19 января 2018

Если вас беспокоит скорость, то вы также можете рассмотреть WebGL.

0 голосов
/ 06 июня 2017

Быстрый HTML-код демонстрации: Исходя из того, что я знаю о графической библиотеке SFML C ++:

Сохраните это как файл HTML с кодировкой UTF-8 и запустите его. Не стесняйтесь рефакторинг, мне просто нравится использовать японские переменные, потому что они лаконичны и не занимают много места

Редко вы хотите установить один произвольный пиксель и отобразить это на экране. Так что используйте

PutPix(x,y, r,g,b,a) 

метод для отрисовки множества произвольных пикселей в резервный буфер. (дешевые звонки)

Затем, когда будете готовы показать, позвоните по номеру

Apply() 

метод для отображения изменений. (дорогой звонок)

Полный код файла .HTML ниже:

<!DOCTYPE HTML >
<html lang="en">
<head>
    <title> back-buffer demo </title>
</head>
<body>

</body>

<script>
//Main function to execute once 
//all script is loaded:
function main(){

    //Create a canvas:
    var canvas;
    canvas = attachCanvasToDom();

    //Do the pixel setting test:
    var test_type = FAST_TEST;
    backBufferTest(canvas, test_type);
}

//Constants:
var SLOW_TEST = 1;
var FAST_TEST = 2;


function attachCanvasToDom(){
    //Canvas Creation:
    //cccccccccccccccccccccccccccccccccccccccccc//
    //Create Canvas and append to body:
    var can = document.createElement('canvas');
    document.body.appendChild(can);

    //Make canvas non-zero in size, 
    //so we can see it:
    can.width = 800;
    can.height= 600;

    //Get the context, fill canvas to get visual:
    var ctx = can.getContext("2d");
    ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
    ctx.fillRect(0,0,can.width-1, can.height-1);
    //cccccccccccccccccccccccccccccccccccccccccc//

    //Return the canvas that was created:
    return can;
}

//THIS OBJECT IS SLOOOOOWW!
// 筆 == "pen"
//T筆 == "Type:Pen"
function T筆(canvas){


    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _絵資 = _ctx.createImageData(1,1); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){
        _筆[0]   = r;
        _筆[1]   = g;
        _筆[2]   = b;
        _筆[3]   = a;
        _ctx.putImageData( _絵資, x, y );  
    }
}

//Back-buffer object, for fast pixel setting:
//尻 =="butt,rear" using to mean "back-buffer"
//T尻=="type: back-buffer"
function T尻(canvas){

    //Publicly Exposed Functions
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//
    this.PutPix = _putPix;
    this.Apply  = _apply;
    //PEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPEPE//

    if(!canvas){
        throw("[NilCanvasGivenToPenConstruct]");
    }

    var _can = canvas;
    var _ctx = canvas.getContext("2d");

    //Pixel Setting Test:
    // only do this once per page
    //絵  =="image"
    //資  =="data"
    //絵資=="image data"
    //筆  =="pen"
    var _w = _can.width;
    var _h = _can.height;
    var _絵資 = _ctx.createImageData(_w,_h); 
    // only do this once per page
    var _筆  = _絵資.data;   


    function _putPix(x,y,  r,g,b,a){

        //Convert XY to index:
        var dex = ( (y*4) *_w) + (x*4);

        _筆[dex+0]   = r;
        _筆[dex+1]   = g;
        _筆[dex+2]   = b;
        _筆[dex+3]   = a;

    }

    function _apply(){
        _ctx.putImageData( _絵資, 0,0 );  
    }

}

function backBufferTest(canvas_input, test_type){
    var can = canvas_input; //shorthand var.

    if(test_type==SLOW_TEST){
        var t筆 = new T筆( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t筆.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

    }else
    if(test_type==FAST_TEST){
        var t尻 = new T尻( can );

        //Iterate over entire canvas, 
        //and set pixels:
        var x0 = 0;
        var x1 = can.width - 1;

        var y0 = 0;
        var y1 = can.height -1;

        for(var x = x0; x <= x1; x++){
        for(var y = y0; y <= y1; y++){
            t尻.PutPix(
                x,y, 
                x%256, y%256,(x+y)%256, 255
            );
        }}//next X/Y

        //When done setting arbitrary pixels,
        //use the apply method to show them 
        //on screen:
        t尻.Apply();

    }
}


main();
</script>
</html>
0 голосов
/ 23 апреля 2016

putImageData вероятно быстрее, чем fillRect изначально.Я думаю, что это потому, что пятый параметр может иметь различные способы назначения (цвет прямоугольника), используя строку, которая должна быть интерпретирована.

Предположим, вы делаете это:

context.fillRect(x, y, 1, 1, "#fff")
context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`
context.fillRect(x, y, 1, 1, "rgb(255,255,255)")`
context.fillRect(x, y, 1, 1, "blue")`

Итак, линия

context.fillRect(x, y, 1, 1, "rgba(255, 255, 255, 0.5)")`

самая тяжелая из всех.Пятый аргумент в вызове fillRect является немного более длинной строкой.

...