Make a bitmap wrap around the canvas for infinite scrolling

前端 未结 3 638
北恋
北恋 2020-12-12 02:56

I am looking for a way to wrap a bitmap image around the canvas, for an infinite scrolling effect. I\'m looking at EaselJS but clean javascript code will also suffice.

相关标签:
3条回答
  • 2020-12-12 03:09

    Start with a good landscape image.

    enter image description here

    Flip the image horizontally using context.scale(-1,1).

    enter image description here

    Combine the flipped image to the right side of the original image.

    enter image description here

    Because we have exactly mirrored the images, the far left and right sides of the combined image are exactly the same.

    Therefore, as we pan across the combined image and “run out of image”, we can just add another copy of the combined image to the right side and we have infinite + seamless panning.

    Here's code and a Fiddle: http://jsfiddle.net/m1erickson/ywDp5/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    <style>
        body{ background-color: ivory; }
        canvas{border:1px solid red;}
    </style>
    
    <script>
    $(function(){
    
        // thanks Paul Irish for this RAF fallback shim
        window.requestAnimFrame = (function(callback) {
          return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
          function(callback) {
            window.setTimeout(callback, 1000 / 60);
          };
        })();
    
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        var infiniteImage;
        var infiniteImageWidth;
        var img=document.createElement("img");
        img.onload=function(){
    
          // use a tempCanvas to create a horizontal mirror image
          // This makes the panning appear seamless when
          // transitioning to a new image on the right
          var tempCanvas=document.createElement("canvas");
          var tempCtx=tempCanvas.getContext("2d");
          tempCanvas.width=img.width*2;
          tempCanvas.height=img.height;
          tempCtx.drawImage(img,0,0);
          tempCtx.save();
          tempCtx.translate(tempCanvas.width,0);
          tempCtx.scale(-1,1);
          tempCtx.drawImage(img,0,0);
          tempCtx.restore();
          infiniteImageWidth=img.width*2;
          infiniteImage=document.createElement("img");
          infiniteImage.onload=function(){
              pan();
          }
          infiniteImage.src=tempCanvas.toDataURL();
        }
        img.crossOrigin="anonymous";
        img.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/mountain.jpg";
    
    
        var fps = 60;
        var offsetLeft=0;
        function pan() {
    
            // increase the left offset
            offsetLeft+=1;
            if(offsetLeft>infiniteImageWidth){ offsetLeft=0; }
    
            ctx.drawImage(infiniteImage,-offsetLeft,0);
            ctx.drawImage(infiniteImage,infiniteImage.width-offsetLeft,0);
    
            setTimeout(function() {
                requestAnimFrame(pan);
            }, 1000 / fps);
        }
    
    }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <canvas id="canvas" width=500 height=143></canvas><br>
    </body>
    </html>
    
    0 讨论(0)
  • 2020-12-12 03:13

    You can achieve this quite easily with the html5 canvas.
    Look at the drawImage specification here :
    http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#drawing-images

    drawImage comes in 3 flavors, the first being a simple copy of the image, the second allowing to scale the image, and the third is the one you seek, it allows to perform clipping in a single call.

    What you have to do :
    - have a counter that will move from zero to the width of your image, then loop to zero again.
    - each frame, draw the maximum of the image that you can on the canvas.
    - If there is still some part of the canvas not drawn, draw again the image starting from zero to fill the canvas.

    i made a fiddle, the only part that matters is in the animate function (other things are tools i often use in my fiddles).
    (Rq : I assumed in this example that two images would be enough to fill the canvas.)

    http://jsfiddle.net/gamealchemist/5VJhp/

    var startx = Math.round(startPos);
    var clippedWidth = Math.min(landscape.width - startx, canvasWidth);
    
    // fill left part of canvas with (clipped) image.
    ctx.drawImage(landscape, startx, 0, clippedWidth, landscape.height,
    0, 0, clippedWidth, landscape.height);
    
    if (clippedWidth < canvasWidth) {
        // if we do not fill the canvas
        var remaining = canvasWidth - clippedWidth;
        ctx.drawImage(landscape, 0, 0, remaining, landscape.height,
        clippedWidth, 0, remaining, landscape.height);
    }
    // have the start position move and loop
    startPos += dt * rotSpeed;
    startPos %= landscape.width;
    
    0 讨论(0)
  • 2020-12-12 03:18

    To answer my own question: I found a way to achieve this effect with EaselJS. The great benefit of this method is that you don't have to check for the position of your bitmap. It will scroll infinitely - without ever resetting the position to 0.

    The trick is to fill a shape with a bitmapfill. You can set a bitmapfill to repeat infinitely. Then you use a Matrix2D to decide the offset of the bitmapfill. Since it repeats automatically, this will generate a scrolling effect.

    function.createRoad() {
        // road has a matrix, shape and image
        _m = new createjs.Matrix2D();
        // this gets the image from the preloader - but this can be any image
        _r = queue.getResult("road");
        // this creates a shape that will hold the repeating bitmap
         _roadshape = new createjs.Shape();
        // put the shape on the canvas
        addChild(_roadshape);
    }
    //
    // the draw code gets repeatedly called, for example by requestanimationframe
    //
    function.drawRoad() {
        // var _speed = 4;
        _m.translate(-_speed, 0);
        _roadshape.graphics.clear().beginBitmapFill(_r, "repeat", _m).rect(0, 0, 900, 400);
    }
    
    0 讨论(0)
提交回复
热议问题