Javascript генерирует прозрачный пиксель 1X1 в формате dataURL

Я хотел бы знать способ генерации одного пикселя в JavaScript, который преобразует его в base64. Идеальная функция:

function createPixel(hexColor, opacity){
   //...Calculate
   return base64DataURL;
}

Я не очень хорошо разбираюсь в обработке изображений. Любой формат в порядке (png, gif и т.д.). Я хотел бы использовать это для наложения фоновых изображений (да, я мог бы использовать rgba css3, но я пытаюсь разместить его с фоновым изображением только на одном элементе, поэтому я не накладываю элемент поверх другого для достижения эффекта).

Спасибо заранее.

Изменить: я бы не хотел использовать canvas, я уверен, что вы можете использовать canvas для получения base64 dataURL, но я уверен, что это не так быстро, как манипуляция строкой. Также я не беспокоюсь о преобразовании изображения в base64, но больше заинтересован в создании изображения.

Ответ 1

Здесь реализована полностью совместимая с кросс-браузером реализация с использованием <canvas> (demo @jsfiddle).

var canvas = document.createElement('canvas');

// http://code.google.com/p/explorercanvas/wiki/Instructions#Dynamically_created_elements
if (!canvas.getContext) G_vmlCanvasManager.initElement(canvas);

var ctx = canvas.getContext('2d');
canvas.width = 1;
canvas.height = 1;

// for simplicity, assume the input is in rgba format
function createPixel(r, g, b, a) {
    ctx.fillStyle = 'rgba(' + [r,g,b,a].join() + ')';
    ctx.fillRect(0,0,1,1);
    // 'data:image/png;base64,'.length => 22
    return canvas.toDataURL('image/png','').substring(22);
}

Мне было любопытно посмотреть, как это складывается против icktoofay answer, с точки зрения производительности. Обратите внимание, что для IE < 9 это должно будет использовать excanvas, что означает, что производительность почти наверняка будет там хуже (но что нового).

Проверьте jsperf: http://jsperf.com/base64image

Ответ 2

function createPlaceholder(w, h) {
    var img = document.createElement('img');
    img.setAttribute('style', 'width:'+w+'px;height:'+h+'px;border:none;display:block');
    img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
    return img;
}

(для прозрачного пикселя)

Ответ 3

Спустя 4 года здесь более простое решение, которое генерирует стандартный GIF, поэтому фактически работает в браузерах (я не мог заставить PEM-решение работать во что угодно) и на порядок быстрее, чем PEM/canvas. Единственным недостатком является то, что GIF не поддерживает альфа-непрозрачность - но это можно контролировать с помощью CSS.

Он основан на этот JSFiddle (неизвестный красивый автор), но с базовой оптимизацией - повторно используется keyStr и принимает как hex строка ('# FF0000') и hex literal (0xFF0000) - последняя намного быстрее (спасибо icktoofay).

<html>
<body onload="Test()">
<script>
function Test() {
    var img = new Image;
    img.src = createPixelGIF(0xff0000); // generate a red pixel data URI
    img.height = 100; img.width = 100;  // optional: add dimensions
    document.body.appendChild(img);     // add to the page
}

// ROUTINES =============

var createPixelGIF = (function() {

    var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    return function createPixelGIF(hexColor) {
        return "data:image/gif;base64,R0lGODlhAQABAPAA" + encodeHex(hexColor) + "/yH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==";
    }

    function encodeHex(hexColor) {
        var rgb;
        if(typeof hexColor == 'string') {
            var s = hexColor.substring(1, 7);
            if (s.length < 6) s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
            rgb = [ parseInt(s[0] + s[1], 16), parseInt(s[2] + s[3], 16), parseInt(s[4] + s[5], 16) ];
        }
        else
            rgb = [ (hexColor & (0xFF << 16)) >> 16, (hexColor & (0xFF << 8)) >> 8, hexColor & 0xFF ];

        return encodeRGB(rgb[0], rgb[1], rgb[2]);
    }

    function encodeRGB(r, g, b) {
        return encode_triplet(0, r, g) + encode_triplet(b, 255, 255);
    }

    function encode_triplet(e1, e2, e3) {
        enc1 = e1 >> 2;
        enc2 = ((e1 & 3) << 4) | (e2 >> 4);
        enc3 = ((e2 & 15) << 2) | (e3 >> 6);
        enc4 = e3 & 63;
        return keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4);
    }

})();
</script>
</body>
</html>


Обновлены результаты JSPerf: http://jsperf.com/base64image/4 (код выше - "createPixelGIF2" ). Вы увидите, что я попробовал дальнейшую оптимизацию (3 + 4), но, похоже, JS более счастлив с операциями стека, чем компромиссные функции, которые трудно читать:)

Я также добавил обновленный тест для метода canvas, который по какой-то причине исключил создание объекта canvas - самого большого сопротивления производительности, которое можно было бы увидеть в реальном мире.

Ответ 4

Попробуйте это. Он использует несколько эзотерический формат изображения (PAM), но вы сказали, что любой формат в порядке, и он действительно работает! Он не оптимизирован или что-то еще, поэтому он, вероятно, довольно медленный, но эй, он работает.

Изменить: Хорошо, я немного оптимизировал его...

var createPixel=(function() {
    var table=[];
    for(var i=0;i<26;i++) {
        table.push(String.fromCharCode("A".charCodeAt(0)+i));
    }
    for(var i=0;i<26;i++) {
        table.push(String.fromCharCode("a".charCodeAt(0)+i));
    }
    for(var i=0;i<10;i++) {
        table.push(i.toString(10));
    }
    table.push("+");
    table.push("/");
    function b64encode(x) {
        var bits=[];
        for(var i=0;i<x.length;i++) {
            var byte=x.charCodeAt(i);
            for(var j=7;j>=0;j--) {
                bits.push(byte&(1<<j));
            }
        }
        var output=[];
        for(var i=0;i<bits.length;i+=6) {
            var section=bits.slice(i, i+6).map(
                function(bit) { return bit?1:0; });
            var required=6-section.length;
            while(section.length<6) section.push(0);
            section=(section[0]<<5)|
                (section[1]<<4)|
                (section[2]<<3)|
                (section[3]<<2)|
                (section[4]<<1)|
                section[5];
            output.push(table[section]);
            if(required==2) {
                output.push('=');
            }else if(required==4) {
                output.push('==');
            }
        }
        output=output.join("");
        return output;
    }
    if(window.btoa) {
        b64encode=window.btoa;
    }
    return function createPixel(hexColor, opacity) {
        var colorTuple=[
            (hexColor&(0xFF<<16))>>16,
            (hexColor&(0xFF<<8))>>8,
            hexColor&0xFF,
            Math.floor(opacity*255)
        ];
        var data="P7\nWIDTH 1\nHEIGHT 1\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n";
        colorTuple.forEach(function(tupleElement) {
            data+=String.fromCharCode(tupleElement);
        });
        var base64DataURL="data:image/pam;base64,"+b64encode(data);
        return base64DataURL;
    }
})();

... но действительно, canvas должен работать нормально.