开发者

Nine-patch images for web development [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.

We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.

Closed 2 years ago.

开发者_StackOverflow社区 Improve this question

I'm wondering if there is something like 9-patch in Android, but for web development?

Disclosure: I have no idea about web development at all, but I was curious to know if it exists. And a simple web search with the term 9-patch didn't bring up any related results, so I figured it has either another term or it doesn't exist or is not used widely enough.

Anyone knows?


Yes. It is used for border-image in CSS 3:

http://www.css3.info/preview/border-image/

http://www.w3.org/TR/css3-background/#border-images


If you're still interested I created a Javascript file that uses canvas to create real nine patch image support for the web. The open source project can be found here:

https://github.com/chrislondon/9-Patch-Image-for-Websites


Well, I took the trouble to correct deserts errors I found in the link above.

Knowing NinePath android is a useful tool adding dynamic painting and recognition of padding (which was missing in the previous pluying). I could add one few scripts for complete functionality.

Replace the following code in the library path9.js!

function NinePatchGetStyle(element, style)
{
    if (window.getComputedStyle)
    {
        var computedStyle = window.getComputedStyle(element, "");
        if (computedStyle === null)
            return "";
        return computedStyle.getPropertyValue(style);
    }
    else if (element.currentStyle)
    {
        return element.currentStyle[style];
    }
    else
    {
        return "";
    }
}

// Cross browser function to find valid property
function NinePatchGetSupportedProp(propArray)
{
    var root = document.documentElement; //reference root element of document
    for (var i = 0; i < propArray.length; i++)
    {
        // loop through possible properties
        if (typeof root.style[propArray[i]] === "string")
        {
            //if the property value is a string (versus undefined)
            return propArray[i]; // return that string
        }
    }
    return false;
}


/**
 * 9patch constructer. Sets up cached data and runs initial draw.
 * @param {Dom Element} div El Elemento dom en donde se pinta el ninepath
 * @param {function} callback La funcion que se llamara cuando se termina 
 * la carga de la imagen y el pintado del elemento div.
 * @returns {NinePatch} Un objeto nine path
 */
function NinePatch(div,callback)
{
    this.div = div;
    this.callback =callback;
    this.padding = {top:0,left:0,right:0,bottom:0};
    // Load 9patch from background-image
    this.bgImage = new Image();
    this.bgImage.src = NinePatchGetStyle(this.div, 'background-image').replace(/"/g, "").replace(/url\(|\)$/ig, "");
    var este = this;

    this.bgImage.onload = function()
    {
        este.originalBgColor = NinePatchGetStyle(este.div, 'background-color');
        este.div.style.background = 'none';

        // Create a temporary canvas to get the 9Patch index data.
        var tempCtx, tempCanvas;
        tempCanvas = document.createElement('canvas');
        tempCanvas.width  = este.bgImage.width;
        tempCanvas.height = este.bgImage.height;
        tempCtx = tempCanvas.getContext('2d');
        tempCtx.drawImage(este.bgImage, 0, 0);

        // Obteniendo el padding lateral derecho
        var dataPad = tempCtx.getImageData(este.bgImage.width-1,0,1,este.bgImage.height).data;
        var padRight = este.getPadBorder(dataPad,este.bgImage.width,este.bgImage.height);
        este.padding.top = padRight.top;
        este.padding.bottom = padRight.bottom;
        dataPad = tempCtx.getImageData(0,este.bgImage.height-1,este.bgImage.width,1).data;
        var padBottom = este.getPadBorder(dataPad,este.bgImage.width,este.bgImage.height);

        este.padding.left = padBottom.top;
        este.padding.right = padBottom.bottom;

        // Loop over each  horizontal pixel and get piece
        var data = tempCtx.getImageData(0, 0, este.bgImage.width, 1).data;

        // Use the upper-left corner to get staticColor, use the upper-right corner
        // to get the repeatColor.
        var tempLength = data.length - 4;
        var staticColor = data[0] + ',' + data[1] + ',' + data[2] + ',' + data[3];
        var repeatColor = data[tempLength] + ',' + data[tempLength + 1] + ',' +
                data[tempLength + 2] + ',' + data[tempLength + 3];

        este.horizontalPieces = este.getPieces(data, staticColor, repeatColor);

        // Loop over each  horizontal pixel and get piece
        data = tempCtx.getImageData(0, 0, 1, este.bgImage.height).data;
        este.verticalPieces = este.getPieces(data, staticColor, repeatColor);

        // use this.horizontalPieces and this.verticalPieces to generate image
        este.draw();
        este.div.onresize = function()
        {
            este.draw();
        };
        if(callback !== undefined)
        {
            if (typeof(callback) === "function")
                callback();
        }
    };
}

// Stores the HTMLDivElement that's using the 9patch image
NinePatch.prototype.div = null;
// Padding
NinePatch.prototype.padding = null;
// Get padding
NinePatch.prototype.callback = null;
// Stores the original background css color to use later
NinePatch.prototype.originalBG = null;
// Stores the pieces used to generate the horizontal layout
NinePatch.prototype.horizontalPieces = null;
// Stores the pieces used to generate the vertical layout
NinePatch.prototype.verticalPieces = null;
// Stores the 9patch image
NinePatch.prototype.bgImage = null;

// Gets the horizontal|vertical pieces based on image data
NinePatch.prototype.getPieces = function(data, staticColor, repeatColor)
{
    var tempDS, tempPosition, tempWidth, tempColor, tempType;
    var tempArray = new Array();

    tempColor = data[4] + ',' + data[5] + ',' + data[6] + ',' + data[7];
    tempDS = (tempColor === staticColor ? 's' : (tempColor === repeatColor ? 'r' : 'd'));
    tempPosition = 1;

    for (var i = 4, n = data.length - 4; i < n; i += 4)
    {
        tempColor = data[i] + ',' + data[i + 1] + ',' + data[i + 2] + ',' + data[i + 3];
        tempType = (tempColor === staticColor ? 's' : (tempColor === repeatColor ? 'r' : 'd'));
        if (tempDS !== tempType)
        {
            // box changed colors
            tempWidth = (i / 4) - tempPosition;
            tempArray.push(new Array(tempDS, tempPosition, tempWidth));

            tempDS = tempType;
            tempPosition = i / 4;
            tempWidth = 1;
        }
    }

    // push end
    tempWidth = (i / 4) - tempPosition;
    tempArray.push(new Array(tempDS, tempPosition, tempWidth));
    return tempArray;
};

NinePatch.prototype.getPadBorder = function(dataPad,width,height)
{
    var staticRight = dataPad[0] + ',' + dataPad[1] + ',' + dataPad[2] + ',' + dataPad[3];
    var pad={top:0,bottom:0};

    // Padding para la parte superior
    for (var i=0;i<dataPad.length;i+=4)
    {
        var tempColor = dataPad[i] + ',' + dataPad[i + 1] + ',' + dataPad[i + 2] + ',' + dataPad[i + 3];
        if(tempColor !==staticRight)
            break;
        pad.top++;
    }
    // padding inferior
    for (var i=dataPad.length-4;i>=0;i-=4)
    {
        var tempColor = dataPad[i] + ',' + dataPad[i + 1] + ',' + dataPad[i + 2] + ',' + dataPad[i + 3];
        if(tempColor !==staticRight)
            break;
        pad.bottom++;
    }
    return pad;
};

// Function to draw the background for the given element size.
NinePatch.prototype.draw = function()
{
    var dCtx, dCanvas, dWidth, dHeight;

    if(this.horizontalPieces === null)
        return;
    dWidth = this.div.offsetWidth;
    dHeight = this.div.offsetHeight;

    dCanvas = document.createElement('canvas');
    dCtx = dCanvas.getContext('2d');

    dCanvas.width = dWidth;
    dCanvas.height = dHeight;

    var fillWidth, fillHeight;

    // Determine the width for the static and dynamic pieces
    var tempStaticWidth = 0;
    var tempDynamicCount = 0;

    for (var i = 0, n = this.horizontalPieces.length; i < n; i++)
    {
        if (this.horizontalPieces[i][0] === 's')
            tempStaticWidth += this.horizontalPieces[i][2];
        else
            tempDynamicCount++;
    }

    fillWidth = (dWidth - tempStaticWidth) / tempDynamicCount;

    // Determine the height for the static and dynamic pieces
    var tempStaticHeight = 0;
    tempDynamicCount = 0;
    for (var i = 0, n = this.verticalPieces.length; i < n; i++)
    {
        if (this.verticalPieces[i][0] === 's')
            tempStaticHeight += this.verticalPieces[i][2];
        else
            tempDynamicCount++;
    }

    fillHeight = (dHeight - tempStaticHeight) / tempDynamicCount;

    // Loop through each of the vertical/horizontal pieces and draw on
    // the canvas
    for (var i = 0, m = this.verticalPieces.length; i < m; i++)
    {
        for (var j = 0, n = this.horizontalPieces.length; j < n; j++)
        {
            var tempFillWidth, tempFillHeight;

            tempFillWidth = (this.horizontalPieces[j][0] === 'd') ?
                    fillWidth : this.horizontalPieces[j][2];
            tempFillHeight = (this.verticalPieces[i][0] === 'd') ?
                    fillHeight : this.verticalPieces[i][2];

            // Stretching :
            if (this.verticalPieces[i][0] !== 'r') {
                // Stretching is the same function for the static squares
                // the only difference is the widths/heights are the same.
                dCtx.drawImage(
                        this.bgImage,
                        this.horizontalPieces[j][1], this.verticalPieces[i][1],
                        this.horizontalPieces[j][2], this.verticalPieces[i][2],
                        0, 0,
                        tempFillWidth, tempFillHeight);
            } else {
                var tempCanvas = document.createElement('canvas');
                tempCanvas.width = this.horizontalPieces[j][2];
                tempCanvas.height = this.verticalPieces[i][2];

                var tempCtx = tempCanvas.getContext('2d');
                tempCtx.drawImage(this.bgImage,
                        this.horizontalPieces[j][1], this.verticalPieces[i][1],
                        this.horizontalPieces[j][2], this.verticalPieces[i][2],
                        0, 0,
                        this.horizontalPieces[j][2], this.verticalPieces[i][2]);

                var tempPattern = dCtx.createPattern(tempCanvas, 'repeat');
                dCtx.fillStyle = tempPattern;
                dCtx.fillRect(
                        0, 0,
                        tempFillWidth, tempFillHeight);
            }

            // Shift to next x position
            dCtx.translate(tempFillWidth, 0);
        }

        // shift back to 0 x and down to the next line
        dCtx.translate(-dWidth, (this.verticalPieces[i][0] === 's' ? this.verticalPieces[i][2] : fillHeight));
    }

    // store the canvas as the div's background
    var url = dCanvas.toDataURL("image/png");
    var tempIMG = new Image();

    var _this = this;
    tempIMG.onload = function(event)
    {
        _this.div.style.background = _this.originalBgColor + " url(" + url + ") no-repeat";
    };
    tempIMG.src = url;
};


The usage is the following:


    var elemDom = document.getElementById("idDiv");
    var background = "border.9.png";
    if (background.match(/\.9\.(png|gif)/i)) // Es nine path?
    {
        elemDom.style.backgroundRepeat = "no-repeat";
        elemDom.style.backgroundPosition = "-1000px -1000px";
        elemDom.style.backgroundImage = "url('"+background+"')";
        var ninePatch = new NinePatch(elemDom,function()
        {
            elemDom.style.paddingLeft = ninePatch.padding.left;
            elemDom.style.paddingTop = ninePatch.padding.top;
            elemDom.style.paddingRight = ninePatch.padding.right;
            elemDom.style.paddingBottom = ninePatch.padding.bottom;
        });
    }


I forked https://github.com/chrislondon/9-Patch-Image-for-Websites and fixed the bugs based on the above comments. Now the 9-Patch javascript works well. Please check out https://github.com/blackmonkey/jQuery-9-Patch

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜