【问题标题】:Use method to draw an image使用方法绘制图像
【发布时间】:2014-05-22 18:12:00
【问题描述】:

我目前正在学习 JavaScript OOP。在这里,我创建了一个名为 person 的构造函数。我给它一个原型属性来在画布上绘制每个新人的脸(我的意思是图像)。我引入了不同的警报命令来检查它是否工作。我认为我做的一切都是正确的,但由于某种原因,图像没有被绘制在画布上。

JavaScript

function draw(){

    var canvas = document.getElementById("mycanvas");
    var ctx = canvas.getContext('2d');
    canvas.width = 600;
    canvas.height = 600;
    canvas.style.border = "1px solid black";
    var person = function (name, age) {
       this.name = name;
       this.age = age;
    };

    person.prototype.drawit = function(src, xpos, ypos, width, height) {
        this.src = src;
        this.xpos = xpos;
        this.ypos = ypos;
        this.width = width;
        this.height = height;

        var img = new Image();
        img.src = this.src;
        ctx.drawImage(img, this.xpos, this.ypos, this.width, this.height);
    };

    var person1 = new person("winsteen", "14");
    person1.drawit("lol1", 200, 200, 40, 50);
    alert(person1.age);
    alert(person1.src);
}

window.onload = draw;

HTML

<canvas id="mycanvas"></canvas>

【问题讨论】:

  • 检查你的控制台,有没有错误?
  • 它没有给出任何错误..
  • 您确定"lol1" 是您图像的正确相对路径吗?它似乎不包含文件扩展名(例如 jpg、png、gif)
  • 暂时没有考虑过这个问题。也没有错误报告。添加 .jpg 扩展名完美地绘制了图像:))。

标签: javascript oop canvas


【解决方案1】:

这是因为您的图像需要时间来加载,并且您在加载之前绘制图像。设置img.src 后,将img.onload 设置为执行绘图的函数。一旦图像加载并准备好绘制,该函数将被异步调用。

如果您想使用 this 关键字,您需要绑定您的负载处理程序,因为正如 Paul 所说,否则它会引用图像。

img.onload = drawIt.bind(this);
function drawIt(){
   // Your drawing method, doing nothing but drawing the image to the canvas.
}

我注意到您的代码中有许多其他内容可以更改。目前你的很多代码都是多余的,你不需要在你的drawIt方法中传递xpos、ypos、width、height等。它应该设置/存储在您的对象构造函数中,因此您的 drawIt 方法在没有参数的情况下被调用,并将根据存储在您的对象中的值来绘制它。

如果您想要一种方法来设置这些值,请使用另一种方法来设置它们。

设置/加载图像也是如此。您不想在每次绘制时都创建新图像并加载它。创建一个单独的“loadImage”方法来加载图像并在图像加载后调用drawIt(如我上面的示例所示)。

何时使用bind()

您想使用this 关键字来访问实例变量并创建在许多不同对象上工作相同的函数,因为'this.width' 指的是Person.widthStreet.width 或基本上任何宽度,取决于函数的上下文。方法保持不变,this 会改变。

但是如果您使用事件侦听器this 可能不会总是像您期望的那样运行。 bind() 是一种您可以在任何函数上使用的方法,它会将该函数永久替换为一个新函数,该函数将始终在您作为bind() 的第一个参数提供的上下文中执行。 p>

换句话说:您创建一个新的函数副本,其中this 被永久设置为特定对象。在这种情况下,它将是您的 Person。

【讨论】:

  • 我使用了 img.onload=function(){ ctx.drawImage(img,this.xpos,this.ypos,this.width,this.height);可以吗?它也不起作用
  • 1.在 src 之前设置 onload,2. 在 load 处理程序中,this 指的是 &lt;img&gt;,3. 确保你实际上可以看到您要绘制的内容;即白底白字=不可见
  • ok 做了这样的事情 img.onload=function(){ img.src=this.src; ctx.drawImage(img,this.xpos,this.ypos,this.width,this.height); } 仍然没有图像。它是一个黄色的笑脸。所以在这里没有机会隐形
  • bind()具体是做什么的?我这里第一次遇到...
  • @user3138436 bind 创建函数的副本。副本中this的值与第一个参数bind的值相同,与调用方式无关
【解决方案2】:
  1. 图像必须先加载,然后才能绘制。
  2. &lt;img&gt; 的处理程序中,this 将是&lt;img&gt;
  3. 在调试时检查使用高对比度,这样你就不会得到例如白底白字。

稍微重构一下,你会得到这样的结果:

(function () { // Immediately-invoked_function_expression (IIFE) keeps
               // global namespace clean - see
               // <https://en.wikipedia.org/wiki/Immediately-invoked_function_expression>.
    function draw() {
        var canvas = document.getElementById('myCanvas'),
                     ctx = canvas.getContext('2d');
        canvas.width = 600;
        canvas.height = 600;
        canvas.style.border = "1px solid black";

        function Person(name, age) { // Constructors usually start with a capital letter
            this.name = name;
            this.age = age;
        }

        Person.prototype = {
            constructor: Person // Good to let instances know who they are
        };

        Person.prototype.drawit = function (src, dx, dy, dw, dh) {
            var img;
            this.src = src;
            // Etc. if you want to store these
            img = new Image();
            img.addEventListener('load', function () {
                // `this` here means `img`, access the other values directly
                // from their variables.
                ctx.drawImage(this, dx, dy, dw, dh);
            });
            img.src = src; // Initiate image load
        };

        // Finished setup, now use
        var person1 = new Person("Winsteen", "14");
        person1.drawit("lol1", 200, 200, 40, 50);
    }
    window.addEventListener('load', draw); // Safe wait for page load
}()); // Invoke the IIFE

【讨论】:

  • 我不认为给他一个可复制粘贴的解决方案,其中充满了中间 javascript 概念,例如注册事件处理程序、立即执行匿名函数保持全局命名空间清洁,他将无法理解真正帮助他在他刚开始的时候。
  • 我很感激。指出了很多新的关键词要从这里学习:)。谢谢大家帮助我。
  • @Winchestro 我怀疑 OP 只是想加载一个“人”,如果他们只是复制/粘贴这个而不试图了解发生了什么,他们不太可能改变它做他们想做的一切。我已经包含了 cmets 以帮助他们在阅读时指出正确的方向。正如 OP 所说,他们正在学习 JavaScript 中的 OOP,而不是说他们对整个语言都是新手,我希望其他概念(IIFE、事件处理程序)几乎不需要新的学习即可达到。也就是说,我了解简单的复制/粘贴对您的学习没有帮助:(
  • 很公平 :) 我尝试根据我在特定问题中看到的潜力/兴趣来平衡示例与解释。如果有人显然只是需要一个快速的解决方案(因为 js 不是主要兴趣),我会发布一些工作代码,否则我会尝试尽可能多地解释并尽可能多地自己弄清楚/编写。但我总是很好奇其他人是如何看待它的,他们在这个网站上的时间比我长,因为我想提高我教授/交流概念的能力:)
  • @Winchestro 解释很多并让他们做很多事情很好,但我发现如果答案不包含某些代码的任何示例,他们可以放弃并停止有兴趣,要完美平衡可能会很困难
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-10
  • 1970-01-01
  • 1970-01-01
  • 2017-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多