使用Canvas的drawImage将图像发送到画布的背面

时间:2014-08-13 22:28:31

标签: javascript html5 canvas

我一直在尝试使用画布,使用线条形状文本等创建绘图,以及插入.png文件。插入.png文件是我无法工作的。

编辑: 这段代码不受欢迎的行为:我将形状加载到图形上下文,然后将图像文件加载到图形上下文,但是当绘制图形上下文时,图像位于形状的后面,尽管最后绘制。 我希望图像文件位于形状前面的顶部。

所需行为:将图像文件放在画布的前面,因此不会被图形上下文中绘制的形状隐藏。

function loadImage(name) {
    images[name] = new Image();
    images[name].src = "DogWalking/" + name + ".png";
    images[name].onload = function() {
        graphics.drawImage(this, 0, 300);
        canvas.bringToFront(this);
    };
}

此处调用绘图功能:

function draw() {
    graphics.save();  // to make sure changes don't carry over from one call to the next
    graphics.fillStyle = "transparent";  // background color
    graphics.fillRect(0,0,wWidth, wHeight);
    graphics.fillStyle = "black";
    applyLimits(graphics,xleft,xright,ytop,ybottom,true);
    graphics.lineWidth = pixelSize;

    world.draw(graphics);

    graphics.drawImage(images["dog-walking11"],200,200);

    graphics.restore();
}

整个页面的代码是

<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Hierarchical Modeling 2D</title>
<style>
    #messagediv {
        position: absolute;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        z-index: 0;
        background-color: indigo;
    }
        #canvasdiv {
        position: absolute;
        left: 0;
        top: 0;
        z-index: 10;
        background-color: transparent;
    }
</style>
<script type="text/javascript" src="rgbcolor.js"></script>  
<script type="text/javascript">
    "use strict";
    var totalResources = 17;
    var numResourcesLoaded = 0;

var images = {};

function loadImage(name) {
  images[name] = new Image();
  images[name].src = "DogWalking/" + name + ".png";
  images[name].onload = function() { 
  //
  graphics.drawImage(this, 0, 300);
  canvas.bringToFront(this);
  }
}

    var canvas;    // DOM object corresponding to the canvas
    var graphics;  // 2D graphics context for drawing on the canvas
    var ctx;  // 2D graphics context for drawing on the canvas
    var myNumber = 0, myNumber2 = 0, myInterval,  myInterval2, myelement, thisdiv, printx;
    var mycoords = new Array();
    var pcoords = new Array(); //coordinates of the portal.
    //var pcoords = [[0,0], [50,300], [250,150]]; //coordinates of the portal.
    var nocoords = 2;
    var frameNumber = 0;  // Current frame number.
    var frameNumber2 = 0;
    var sun;
    var sun2;
    var ground;
    var world;
    var pixelSize;
    var wWidth;
    var wHeight;
    var portals = new Array("calendar1","alternativsearch","art1", "directory1");
    var portalsval = new Array();
    var portalsobj;
    var leftj = new Array(3,1,4,2);
    var forwards = "http://www.alternativworld.com";


    // ----------------  Set Page Layout  ----------------


// function to set size of canvas and location of portals       
function pageLayout() {

    var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];

    wWidth = w.innerWidth || e.clientWidth || g.clientWidth;
    wHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;

    // Adjust wWidth and wHeight if ratio does not match scenary 7 by 5.
    if (wWidth/wHeight != 7/5)
        if (wWidth/wHeight > 7/5) {
            var widthPortion = 5 * wWidth/wHeight;
            wWidth = wWidth * 7 / widthPortion;
        } else {
            var heightPortion = 7 * wHeight/wWidth;
            wHeight = wHeight * 5 / heightPortion;
        }

    var widthheight, localerror = false;
    widthheight = Math.min(wWidth, wHeight);

    if(widthheight < 400){
        var localerror = true;
    }

    if (localerror == true)
        alert("Warning, the page size of your browser or your screen resolution may be too small to correctly view this web page.");

    var theCanvas = d.getElementById("theCanvas");
    theCanvas.height = wHeight;
    theCanvas.width = wWidth;

}

//Function to listen to the mouse events and see if a link is selected.
function doMouseDown(evt) {
    var r = canvas.getBoundingClientRect();
    var x = Math.round(evt.clientX - r.left);
    var y = Math.round(evt.clientY - r.top);
alert(evt.clientX+ " " + evt.clientY);
    for (var i = portals.length+1; i >= 0; i--) {
        var p = pcoords[i];
        if (Math.abs(p[0] - x) <= 50 && Math.abs(p[1] - y) <= 50) {
            document.location.href = forwards;
            return;
        } else if (Math.abs(0 - x) <= 50 && Math.abs(0 - y) <= 50){
            document.location.href = "http://www.alternativeuk.co.uk";
            return;
        }
    }
}


    // ----------------  The object-oriented scene graph API  ------------------

    /**
     * The base class for all nodes in the scene graph data structure.
     */
    function SceneGraphNode() {
        this.fillColor = null;   // If non-null, the default fillStyle for this node.
        this.strokeColor = null; // If non-null, the default strokeStyle for this node.
    }
    SceneGraphNode.prototype.doDraw = function(g) {
            // This method is meant to be abstract and must be
            // OVERRIDDEN in any actual object in the scene graph.
            // It is not meant to be called; it is called by draw().
        throw "doDraw not implemented in SceneGraphNode"
    }
    SceneGraphNode.prototype.draw = function(g) {
           // This method should be CALLED to draw the object
           // represented by this SceneGraphNode.  It should NOT
           // ordinarily be overridden in subclasses.
        graphics.save();
        if (this.fillColor) {
            g.fillStyle = this.fillColor;
        }
        if (this.strokeColor) {
            g.strokeStyle = this.strokeColor;
        }
        this.doDraw(g);
        graphics.restore();
    }
    SceneGraphNode.prototype.setFillColor = function(color) {
            // Sets fillColor for this node to color.
            // Color should be a legal CSS color string, or null.
        this.fillColor = color;
        return this;
    }
    SceneGraphNode.prototype.setStrokeColor = function(color) {
            // Sets strokeColor for this node to color.
            // Color should be a legal CSS color string, or null.
        this.strokeColor = color;
        return this;
    }
    SceneGraphNode.prototype.setColor = function(color) {
            // Sets both the fillColor and strokeColor to color.
            // Color should be a legal CSS color string, or null.
        this.fillColor = color;
        this.strokeColor = color;
        return this;
    }

    /**
     *  Defines a subclass, CompoundObject, of SceneGraphNode to represent
     *  an object that is made up of sub-objects.  Initially, there are no
     *  sub-objects.
     */
    function CompoundObject() {
        SceneGraphNode.call(this);  // do superclass initialization
        this.subobjects = [];  // the list of sub-objects of this object
    }
    CompoundObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
    CompoundObject.prototype.add = function(node) {
            // Add node a subobject of this object.  Note that the
            // return value is a reference to this node, to allow chaining
            // of method calls.
        this.subobjects.push(node);
        return this;
    }
    CompoundObject.prototype.doDraw = function(g) {
            // Just call the sub-objects' draw() methods.
        for (var i = 0; i < this.subobjects.length; i++)
            this.subobjects[i].draw(g);
    }

    /**
     *  Define a subclass, TransformedObject, of SceneGraphNode that
     *  represents an object along with a modeling transformation to
     *  be applied to that object.  The object must be specified in
     *  the constructor.  The transformation is specified by calling
     *  the setScale(), setRotate() and setTranslate() methods. Note that
     *  each of these methods returns a reference to the TransformedObject
     *  as its return value, to allow for chaining of method calls.
     *  The modeling transformations are always applied to the object
     *  in the order scale, then rotate, then translate.
     */
    function TransformedObject(object) {
        SceneGraphNode.call(this);  // do superclass initialization
        this.object = object;
        this.rotationInDegrees = 0;
        this.scaleX = 1;
        this.scaleY = 1;
        this.translateX = 0;
        this.translateY = 0;
    }
    TransformedObject.prototype = new SceneGraphNode();  // (makes it a subclass!)
    TransformedObject.prototype.setRotation = function(angle) {
           // Set the angle of rotation, measured in DEGREES.  The rotation
           // is always about the origin.
        this.rotationInDegrees = angle;
        return this;
    }
    TransformedObject.prototype.setScale = function(sx, sy) {
           // Sets scaling factors.
        this.scaleX = sx;
        this.scaleY = sy;
        return this;
    }
    TransformedObject.prototype.setTranslation = function(dx,dy) {
           // Set translation mounts.
        this.translateX = dx;
        this.translateY = dy;
        return this;
    }
    TransformedObject.prototype.doDraw = function(g) {
            // Draws the object, with its modeling transformation.
        g.save();
        if (this.translateX != 0 || this.translateY != 0) {
            g.translate(this.translateX, this.translateY);
        }
        if (this.rotationInDegrees != 0) {
            g.rotate(this.rotationInDegrees/180*Math.PI);
        }
        if (this.scaleX != 1 || this.scaleY != 1) {
            g.scale(this.scaleX, this.scaleY);
        }
        this.object.draw(g);
        g.restore();
    }

    /**
     *  A subclass of SceneGraphNode representing filled triangles.
     *  The constructor specifies the vertices of the triangle:
     *  (x1,y1), (x2,y2), and (x3,y3).
     */
    function Triangle(x1,y1,x2,y2,x3,y3) {
        SceneGraphNode.call(this);
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
        this.x3 = x3;
        this.y3 = y3;
    }
    Triangle.prototype = new SceneGraphNode();
    Triangle.prototype.doDraw = function(g) {
        g.beginPath();
        g.moveTo(this.x1,this.y1);
        g.lineTo(this.x2,this.y2);
        g.lineTo(this.x3,this.y3);
        g.closePath();
        g.fill();
    }

    /**
     * Directly create a line object as a SceneGraphNode with a
     * custom doDraw() method.  line is of length 1 and
     * extends along the x-axis from (0,0) to (1,0).
     */
    var line = new SceneGraphNode();
    line.doDraw = function(g) {
        g.beginPath();
        g.moveTo(0,0);
        g.lineTo(1,0);
        g.stroke();
    }

    /**
     * Directly create a filled rectangle object as a SceneGraphNode with a
     * custom doDraw() method.  filledRect is a square with side 1, centered
     * at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5).
     */
    var filledRect = new SceneGraphNode();
    filledRect.doDraw = function(g) {
        g.fillRect(-0.5,-0.5,1,1);
    }

    /**
     * Directly create a rectangle object as a SceneGraphNode with a
     * custom doDraw() method.  rect is a square with side 1, centered
     * at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5).  Only the
     * outline of the square is drawn.
     */
    var rect = new SceneGraphNode();
    rect.doDraw = function(g) {
        g.strokeRect(-0.5,-0.5,1,1);
    }

    /**
     * Directly create a filled circle object as a SceneGraphNode with a
     * custom doDraw() method.  filledCircle is a circle with radius 0.5
     * (diameter 1), centered at (0,0).
     */
    var filledCircle = new SceneGraphNode();
    filledCircle.doDraw = function(g) {
        g.beginPath();
        g.arc(0,0,0.5,0,2*Math.PI);
        g.fill();
    }

    var clickHere = new SceneGraphNode();
    clickHere.doDraw = function(g) {
        g.fillText("click here :)",0,0)
    }

    /**
     * Directly create a circle object as a SceneGraphNode with a
     * custom doDraw() method.  filledCircle is a circle with radius 0.5
     * (diameter 1), centered at (0,0).  Only the outline of the circle
     * is drawn.
     */
    var circle = new SceneGraphNode();
    circle.doDraw = function(g) {
        g.beginPath();
        g.arc(0,0,0.5,0,2*Math.PI);
        g.stroke();
    }

    var dog = new SceneGraphNode();
    dog.doDraw = function(g) {
        g.drawImage(images["dog-walking11"],-2, 2);
        alert(images["dog-walking11"].name);
    }


    // -------------------- Specific to this application ----------------------------

    /*
     * Define two extra basic objects as SceneGraphNodes with custom doDraw() methods.
     * One represents the ground, the other a vane for a windmill.
     */
    var ground = new SceneGraphNode();
    ground.doDraw = function(g) {
        g.beginPath();
        g.moveTo(0,-1);
        g.lineTo(0,0.8);
        g.lineTo(1.5,1.65);
        g.lineTo(1.8,1.3);
        g.lineTo(3,2.1);
        g.lineTo(4.7,0.7);
        g.lineTo(6.1,1.2);
        g.lineTo(7,0.8);
        g.lineTo(7,-1);
        g.closePath();
        g.fill();
    }
    var windmillVane = new SceneGraphNode();
    windmillVane.doDraw = function(g) {
        g.beginPath();
        g.moveTo(0,0);
        g.lineTo(0.5,0.1);
        g.lineTo(1.5,0);
        g.lineTo(0.5,-0.1);
        g.closePath();
        g.fill();
    }

    var world;  // A SceneGraphNode representing the entire picture.  This should
                // be created in the createWorld() method.

    var pixelSize;  // The size of one pixel, in the transformed coordinates.
                    //    This is used as the default width of a stroke.

    var background = "#C8C8FF"; // A CSS color string giving the background color.
                                // the draw() function fills the canvas with this color.

    var xleft = 0;   // The requested xy-limits on the canvas, after the 
    var xright = 7;   //    coordinate transformation has been applied.
    var ybottom = -1; //    The transformation is applied in the draw() function.
    var ytop = 4;

    var frameNumber = 0;  // Current frame number.

    var cart;   // TransformedObjects that are animated.
    var wheel;
    var sun;
    var clickText1;
    var clickText2;
    var rotor;


    /**
     *  Create the scene graph data structure.  The global variable world must
     *  refer to the root node of the scene graph.  This function is called in
     *  the init() function.
     */
    function createWorld() {

    pageLayout();

        var i;
        var sunTemp = new CompoundObject();
        sunTemp.setColor("yellow"); // color for filled circle and light rays
        for (i = 0; i < 12; i++) {  // add the 12 light rays, with different rotations
           sunTemp.add( new TransformedObject(line).setScale(0.75,0.75).setRotation(i*30) );
        }
        sunTemp.add( filledCircle );  // the face of the sun
        sunTemp.add( new TransformedObject(circle).setColor("#B40000") ); // outlines the face
        sun = new TransformedObject(sunTemp);

        clickText1 = new TransformedObject(clickHere).setColor("#B40000").setScale(0.01,-0.01);

        var wheelTemp = new CompoundObject();
        wheelTemp.setColor("black"); // color for all but one of the subobjects
        wheelTemp.add( new TransformedObject(filledCircle).setScale(2,2) );
        wheelTemp.add( new TransformedObject(filledCircle).setScale(1.6,1.6).setColor("#CCCCCC") );
        wheelTemp.add( new TransformedObject(filledCircle).setScale(0.4,0.4) );
        for (i = 0; i < 12; i++) {  // add the 12 spokes
           wheelTemp.add( new TransformedObject(line).setRotation(i*30) );
        }
        wheel = new TransformedObject(wheelTemp);

        var cartTemp = new CompoundObject();
        cartTemp.setColor("red"); // color for the rectangular body of the cart
        cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(1.5,-0.1) );
        cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(-1.5,-0.1) );
        cartTemp.add( new TransformedObject(filledRect).setScale(5,2).setTranslation(0,1) ); // the body of the cart
        cart = new TransformedObject(cartTemp).setScale(0.3,0.3);
        clickText2 = new TransformedObject(clickHere).setColor("yellow").setScale(0.01,-0.01);

        var rotorTemp = new CompoundObject(); // a "rotor" consisting of three vanes
        rotorTemp.setColor( "#C86464" ); // color for all of the vanes
        rotorTemp.add( windmillVane );
        rotorTemp.add( new TransformedObject(windmillVane).setRotation(120) );
        rotorTemp.add( new TransformedObject(windmillVane).setRotation(240) );
        rotor = new TransformedObject(rotorTemp);

        var windmill = new CompoundObject();
        windmill.setColor("#E0C8C8"); // color for the pole
        windmill.add( new TransformedObject(filledRect).setScale(0.1,3).setTranslation(0,1.5) ); // the pole
        windmill.add( new TransformedObject(rotor).setTranslation(0,3) ); // the rotating vanes

        world = new CompoundObject();
        world.setColor("#00961E"); // color used for the ground only
        world.add(ground);
        //world.add( new TransformedObject(filledRect).setScale(7,0.8).setTranslation(3.5,0).setColor("#646496") ); // road
        //world.add( new TransformedObject(filledRect).setScale(7,0.06).setTranslation(3.5,0).setColor("white") ); // line in road
        world.add( new TransformedObject(windmill).setScale(0.6,0.6).setTranslation(0.75,1) );
        world.add( new TransformedObject(windmill).setScale(0.4,0.4).setTranslation(2.2,1.3) );
        world.add( new TransformedObject(windmill).setScale(0.7,0.7).setTranslation(3.7,0.8) );
        world.add( new TransformedObject(sun).setTranslation(5.5,3.3) );
        world.add( new TransformedObject(clickText1).setTranslation(5.25,3.3) );
        world.add( cart );
        world.add( clickText2 );

        //alert(2);


    }

    /**
     * This will be called before each frame is drawn.
     */
    function updateFrame() {
        frameNumber++;
        if (frameNumber>= 312){
            frameNumber = 0;
            frameNumber2 = 1;
            }

        cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0);
        clickText2.setTranslation(-3.3 + 13*(frameNumber % 300) / 300.0, 0.25);
        if (typeof(pcoords[5]) != 'undefined') {
            pcoords[5][0] = (-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*canvas.width / (xright-xleft);
            pcoords[5][1] = (0.25-ytop)*canvas.height / (ybottom-ytop);
            }

        wheel.setRotation(-frameNumber*3.1);
        sun.setRotation(-frameNumber);
        rotor.setRotation(frameNumber * 2.7);
    }


    // ------------------------------- graphics support functions --------------------------

    /**
      * Draw one frame of the animation.  Probably doesn't need to be changed,
      * except maybe to change the setting of preserveAspect in applyLimits().
      */
    function draw() {
        graphics.save();  // to make sure changes don't carry over from one call to the next
        graphics.fillStyle = "transparent";  // background color
        graphics.fillRect(0,0,wWidth, wHeight);
        graphics.fillStyle = "black";
        applyLimits(graphics,xleft,xright,ytop,ybottom,true);
        graphics.lineWidth = pixelSize;

world.draw(graphics);

graphics.drawImage(images["dog-walking11"],200,200);

        graphics.restore();
    }

    /**
     * Applies a coordinate transformation to the graphics context, to map
     * xleft,xright,ytop,ybottom to the edges of the canvas.  This is called
     * by draw().  This does not need to be changed.
     */
     //pcoords[0][0] = 
     //pcoords[0][1]=
function applyLimits(g, xleft, xright, ytop, ybottom, preserveAspect) {
    var width = canvas.width;   // The width of this drawing area, in pixels.
    var height = canvas.height; // The height of this drawing area, in pixels.
    var k = portals.length; 
    var j;
    var i = 0, widthheight, myradius;
    var localerror = false;
    if (pcoords.length < k) {
        while (portals[i]){

        j = i + 1;
        if (width > 100){
            var rWidth = width/(k + 1);
            rWidth= Math.floor(rWidth);
        } else {
            var lWidth = 0;
            var rWidth = 0;
        }

        if (height > 100){
            var bHeight = height/(k + 1);
            bHeight= Math.floor(bHeight);
        } else {
            var tHeight = 0;
            var bHeight = 0;
            }

        var myleft = leftj[i] * rWidth - 50;
        var mytop = j * bHeight - 50;

            pcoords[i]= new Array;
            pcoords[i][0] = myleft;
            pcoords[i][1] = mytop;

        i = i + 1;
        }
    }



    if (preserveAspect) {
        // Adjust the limits to match the aspect ratio of the drawing area.
        var displayAspect = Math.abs(height / width);
        var requestedAspect = Math.abs(( ybottom-ytop ) / ( xright-xleft ));
        var excess;
        if (displayAspect > requestedAspect) {
            excess = (ybottom-ytop) * (displayAspect/requestedAspect - 1);
            ybottom += excess/2;
            ytop -= excess/2;
         }
         else if (displayAspect < requestedAspect) {
            excess = (xright-xleft) * (requestedAspect/displayAspect - 1);
            xright += excess/2;
            xleft -= excess/2;
         }
    }
    var pixelWidth = Math.abs(( xright - xleft ) / width);
    var pixelHeight = Math.abs(( ybottom - ytop ) / height);
    pixelSize = Math.min(pixelWidth,pixelHeight);
    if (frameNumber == 4 || frameNumber == 5){
    pcoords.push([(5.25-xleft)*width / (xright-xleft),(3.3-ytop)*height / (ybottom-ytop)]);
    pcoords.push([(-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*width / (xright-xleft), (0.25-ytop)*height / (ybottom-ytop)]);
    }
    g.scale( width / (xright-xleft), height / (ybottom-ytop) );
    g.translate( -xleft, -ytop );
   // if (frameNumber < 3)

}


    //------------------ Animation framework ------------------------------

    var animationTimeout = null; // A null value means the animation is off.
                                 // Otherwise, this is the timeout ID.

    function frame() {
           // Draw one frame of the animation, and schedule the next frame.
        updateFrame();
        draw();
        canvas.addEventListener("mousedown", doMouseDown, false);
        animationTimeout = setTimeout(frame, 33);
    }

    function setAnimationRunning(run) {
        if ( run ) {
            if (animationTimeout == null) {
                    // If the animation is not already running, start
                    // it by scheduling a call to frame().
                animationTimeout = setTimeout(frame, 33);
            }
        }
        else {
            if (animationTimeout != null) {
                    // If the animation is running, stop it by
                    // canceling the next scheduled call to frame().
                clearTimeout(animationTimeout);
            }
            animationTimeout = null; // Indicates that animation is off.
        }
    }

    //----------------------- initialization -------------------------------

    function init() {
        try {
        canvas = document.getElementById("theCanvas");
        if(typeof G_vmlCanvasManager != 'undefined') {
        canvas = G_vmlCanvasManager.initElement(canvas);
    }

            graphics = canvas.getContext("2d");
        }
        catch (e) {
            document.getElementById("message").innerHTML =
                "Sorry, this page requires canvas graphics, but<br>" +
                "it looks like your browser does not support it<br>" +
                "Reported error: " + e;
            return;
        }
        // add any other necessary initialization
        document.getElementById("animateCheck").checked = true; // Make sure box is checked!
        loadImage("dog-walking11");

        createWorld();
        setAnimationRunning(true);  // start the animation
    }
</script>
</head>
<body onload="init()" style="background-color: rgb(220,220,220)">
<div id="messagediv">
    <h2>Hierarchical Modeling Example</h2>

    <!-- For error reporting:  the contents of the noscript tag are
         shown only if JavaScript is not available.  The paragraph with
         id="message" is for reporting errors using JavaScript.-->
    <noscript><b>This page requires JavaScript, which is<br>
       not enabled in your browser.</b></noscript>
    <p id="message" style="color:red"></p>

    <p><input type="checkbox" id="animateCheck" onchange="setAnimationRunning(this.checked)">
         <label for="animateCheck">Run Animation</label>
    </p>
</div>  
<div id="canvasdiv">
<canvas id="theCanvas" width= "400" height= "300"
                style="background-color: transparent"></canvas>

</div>

</body>
</html>

1 个答案:

答案 0 :(得分:0)

在使用graphics.restore()之后,我似乎不得不使用graphics.drawImage()。

虽然我试图以正确的顺序(之后)绘制图像,但与绘制矩形,圆形等相比,在恢复功能()之后,形状不会从缓冲区出来到页面上。

当我调用drawImage时,我认为它正在将其加载到缓冲的画布上,其余部分已准备好以正确的顺序绘制,而事实上它正是将它直接放到页面上。

因为它是用图形上下文调用的,所以看起来很奇怪,所以我认为它会被添加到画布图形的其余部分,但我似乎错了。