【问题标题】:Moving All Images In A Clipped Canvas移动剪切画布中的所有图像
【发布时间】:2013-11-30 13:30:13
【问题描述】:

好的,我刚刚查看了 Canvas 元素,但遇到了问题。

我发现了一个演示,它使用箭头键将三个图像相互叠加。

但是,我剪掉了其中一个,即使上面剪裁的图像移动了,下面的图像现在也没有移动。

HTML:

<canvas id="parallax-canvas">
    Sorry, your browser does not support HTML5 Canvas.
</canvas>

CSS:

.parallax-canvas {
    width: 400px;
    height: 300px;
}

JavaScript:

$(document).ready(function() {
    var w = $("#parallax-canvas").width();
    var h = $("#parallax-canvas").height();

    var sky = new Image();
    sky.src = "assets/img/sky.jpg";
    var skydx = 2;
    var skyx = 0;

    var mountains = new Image();
    mountains.src ="assets/img/mountains.png";
    var mountainsdx = 10;
    var mountainsx = 0;

    var jeep = new Image();
    jeep.src ="assets/img/jeep.png";
    var jeepx = 100; 
    var jeepy = 210; 
    var jeepsx = 0; 
    var jeepsxWidth = 155;

    var cntx =  $("#parallax-canvas")[0].getContext("2d");
    setInterval(draw, 10, cntx);

    $(window).keydown(function(evt) {
        switch (evt.keyCode) {
            case 37: // Left arrow
                if ((skyx + skydx) > skydx)
                  skyx -= skydx;
                else
                  skyx = w;

                if ((mountainsx + mountainsdx) > mountainsdx)
                  mountainsx -= mountainsdx;
                else
                  mountainsx = 398;

                if (jeepsx > 0) 
                    jeepsx -= jeepsxWidth;
                else 
                    jeepsx = (jeepsxWidth*2);

            break;

            case 39: // Right arrow
                if ((skyx + skydx) < (w - skydx))
                  skyx += skydx;
                else
                  skyx = 0;

                if ((mountainsx + mountainsdx) < (w - mountainsdx))
                  mountainsx += mountainsdx;
                else
                  mountainsx = 0;

                if (jeepsx < (jeepsxWidth*2))
                   jeepsx += jeepsxWidth;
                else
                   jeepsx = 0;

                break;
        }
    });

    function draw(_cntx) {
        drawRectangle(_cntx, 0, 0, w, h);
        _cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
        _cntx.beginPath();
        _cntx.moveTo(0,300);
        _cntx.lineTo(150,150);
        _cntx.lineTo(300, 300);
        _cntx.closePath();
        _cntx.clip();
        _cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
        _cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);
    }

    function drawRectangle(_cntx, x, y, w, h) {
        _cntx.beginPath();
        _cntx.rect(x,y,w,h);
        _cntx.closePath();
        _cntx.fill();
        _cntx.stroke();
    }
});

我添加剪辑的部分是这个部分:

function draw(_cntx) {
    drawRectangle(_cntx, 0, 0, w, h);
    _cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
    _cntx.beginPath();
    _cntx.moveTo(0,300);
    _cntx.lineTo(150,150);
    _cntx.lineTo(300, 300);
    _cntx.closePath();
    _cntx.clip();
    _cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
    _cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);
}

如果您从 beginPath 方法中删除并将其包含在 clip 方法中,它将允许三个图像全部移动。

发生了什么:按左右箭头键时天空图像不移动,山和吉普车被剪裁,但在剪裁区域内移动。

我想要发生的事情:要移动的天空图像以及剪辑区域中的山和吉普车。

还有:

我想知道如何使用箭头键移动剪切区域。我的意思是,目前有一个剪切区域,通过左右按图像移动,但剪切(剪切)区域保持静止。我想知道(如果可能的话)如何使用箭头键移动剪切区域。

正在使用的演示:http://www.ibm.com/developerworks/web/library/wa-parallaxprocessing/

不幸的是,由于图像不在网站上,因此无法设置 Fiddle,因此无法提供 URL,但 .zip 在网站上,并通过复制和粘贴其方法位于 JavaScript 文件中的代码的底部部分,即我得到了什么。

感谢您的帮助!

编辑:感谢 Ken 在缩短代码和提高效率方面的帮助。到目前为止,还没有回答我最初关于如何移动图像以及剪切位置的问题。

【问题讨论】:

  • JScript 与 JavaScript
  • 不错的汤姆,原谅我现在有点累了,哈哈。
  • 你能为我们设置一个小提琴吗?
  • 不幸的是,我使用的演示 (ibm.com/developerworks/web/library/wa-parallaxprocessing) 在网站上实际上没有图像,因此我无法在 Fiddle 上为它们提供 URL。但是(我知道这有点问)但是在该链接的底部可以下载.zip,并通过复制和粘贴“beginPath”方法,包括我的代码底部的“clip”方法发布你可以看到我得到了什么。
  • 您可以将图像上传到 imgur.com,但我提供了一个简单的答案。看看到目前为止是否对您有所帮助。

标签: javascript html canvas clip


【解决方案1】:

你需要为你的画布设置一个大小 - 不要使用 CSS 来set the size of a canvas

<canvas id="parallax-canvas" width=500 height=300>
    Sorry, your browser does not support HTML5 Canvas.
</canvas>

同样你需要从画布上读取合适的尺寸:

$(document).ready(function() {
    var canvas = $("#parallax-canvas")[0];
    var w = canvas.width;
    var h = canvas.height;
    ...

每次点击光标键时,您都需要重新绘制所有内容。画布不知道画了什么——它只是一堆像素,所以我们需要自己提供更新的逻辑。

Update 似乎我错过了您正在从 setInterval 循环进行重绘,所以这并不适用,但我让示例保留,因为它可能是一个更好的方法,因为它只是当某些事情实际发生变化时需要更新。例如,一直重绘所有内容的循环会很快耗尽电池。

例如:

$(window).keydown(function(evt) {
    switch (evt.keyCode) {
        case 37: // Left arrow
            if ((skyx + skydx) > skydx)
              skyx -= skydx;
            else
              skyx = w;

            if ((mountainsx + mountainsdx) > mountainsdx)
              mountainsx -= mountainsdx;
            else
              mountainsx = 398;

            if (jeepsx > 0) 
                jeepsx -= jeepsxWidth;
            else 
                jeepsx = (jeepsxWidth*2);

        draw(cntx );

        break;
        ...

对于其他动作,您也需要重复此操作。

由于加载是异步的,您在加载图像的方式上也会遇到问题。您需要通过点击onload 处理程序来处理加载:

var sky = new Image();
sky.onload = functionToHandleLoad;
sky.src = "assets/img/sky.jpg";

当您加载许多图像时,您需要count the images 或使用批量加载程序,例如我的YAIL loader

对于剪辑,使用保存/恢复很重要,因为当前浏览器不能很好地处理剪辑区域的手动重置:

function draw(_cntx) {
    drawRectangle(_cntx, 0, 0, w, h);
    _cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
    _cntx.beginPath();
    _cntx.moveTo(0,300);
    _cntx.lineTo(150,150);
    _cntx.lineTo(300, 300);
    _cntx.closePath();

    _cntx.save();     /// save current clip region

    _cntx.clip();
    _cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
    _cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);

    _cntx.restore();  /// reset clip
}

在回答那里没有可用的小提琴时,所以我没有检查代码的其他部分,但我认为这应该是一个好的开始。

这个功能

function drawRectangle(_cntx, x, y, w, h) {
    _cntx.beginPath();
    _cntx.rect(x,y,w,h);
    _cntx.closePath();
    _cntx.fill();
    _cntx.stroke();
}

可以简化为:

function drawRectangle(_cntx, x, y, w, h) {
    _cntx.fillRect(x, y, w, h);
    _cntx.strokeRect(x, y, w, h);
}

不需要关闭矩形上的路径,并且使用 fillRect/strokeRect 不需要 beginPath。

【讨论】:

  • 感谢肯的回复。感谢您提供有关设置画布大小的链接。下一部分,“同样你需要从画布上读取正确的尺寸”不就是添加了一个不必要的额外行吗?还是我错过了什么?当您说需要重绘所有内容时,我在 keydown 部分之前的行“setInterval(draw, 10, cntx);”每 10 毫秒调用一次 draw 方法,传递 cntx 变量以更新图像。这不符合您所指出的需要定期更新的画布吗? ...
  • ...图片多的问题,难道不能用WaitForImages来解决这个问题吗?并感谢关于保存/恢复剪辑的最后一部分。
  • @user1928743 猜我错过了你使用 setInterval。当没有小提琴时,我倾向于只快速阅读。 w/h 是对您的代码的更正 - 我不知道您使用它的目的是什么,但是如果您想读取画布的宽度/高度,则需要使用属性(而不是 css 大小):)
  • @user1928743 不确定您对 WaitForImages 的含义 - 没有这种开箱即用的方法。正确的方法是处理 onload,因为可能有许多因素会延迟/阻止图像被加载。在处理错误方面,加载程序也可以让生活变得更轻松。
  • 查看了您的更新方法,它更有效。它可能解释了为什么我的计算机运行页面有点迟缓,特别是当演示每 10 毫秒更新一次时。感谢那。查看加载程序并了解为什么这是必要的,以及缩短的代码,再次感谢您。但是我不认为您对为什么每个图像都没有移动有任何见解?还有我关于如何在更新时移动裁剪区域点的其他查询?谢谢你的帮助,肯。
猜你喜欢
  • 2017-03-06
  • 2019-05-11
  • 2013-04-05
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 2017-05-06
  • 1970-01-01
相关资源
最近更新 更多