Image对象的complete与onload()事件(以及Canvas的drawImage()函数)

问题

之前基本看完了canvas的API,准备做一个小游戏,emmm,然后今天就遇到了问题,我本来以为是canvas的问题,但是仔细分析之后才发现是JS中的问题:

回顾

关于HTML与JS的关系可以从网页的加载顺序来看,当浏览器打开网页的时候,网页由上至下的解析该网页,然后开始构建DOM树(Document Object Model)(属于JavaScript),也就是说将HTML的每一个标签构建成一个相应的对象,但是DOM整体就是一个对象,每一个标签是其的一个子对象,对比与JSX有些相似,遇到JS或CSS时利用相应引擎进行解析。从这里就可以理解出,在网页完成加载后,每一个标签就成为了DOM这个对象里的一个子对象,我们使用相应的方法,比如Document.getElementById(“xxx”)就可以获取相应的对象,并进行操作。

Image()对象

由上可知,Image()也是一个节点对象,只是是否渲染至页面上的问题。

Image对象的属性

属性 描述 属性 描述
align 设置或返回与内联内容的对齐方式。 align 设置或返回与内联内容的对齐方式。
alt 设置或返回无法显示图像时的替代文本。 alt 设置或返回无法显示图像时的替代文本。
border 设置或返回图像周围的边框。 border 设置或返回图像周围的边框。
complete 返回浏览器是否已完成对图像的加载。 complete 返回浏览器是否已完成对图像的加载。
height 设置或返回图像的高度。 height 设置或返回图像的高度。
hspace 设置或返回图像左侧和右侧的空白。 hspace 设置或返回图像左侧和右侧的空白。
id 设置或返回图像的 id。 id 设置或返回图像的 id。
isMap 返回图像是否是服务器端的图像映射。 isMap 返回图像是否是服务器端的图像映射。
longDesc 设置或返回指向包含图像描述的文档的 URL。 longDesc 设置或返回指向包含图像描述的文档的 URL。
lowsrc 设置或返回指向图像的低分辨率版本的 URL。 lowsrc 设置或返回指向图像的低分辨率版本的 URL。
name 设置或返回图像的名称。 name 设置或返回图像的名称。
src 设置或返回图像的 URL。 src 设置或返回图像的 URL。
useMap 设置或返回客户端图像映射的 usemap 属性的值。 useMap 设置或返回客户端图像映射的 usemap 属性的值。
vspace 设置或返回图像的顶部和底部的空白。 vspace 设置或返回图像的顶部和底部的空白。
width 设置或返回图像的宽度。 width 设置或返回图像的宽度。

标准属性

属性 描述
className 设置或返回元素的 class 属性。
title 设置或返回元素的 title。

Image对象的事件句柄

事件句柄 描述
onabort 当用户放弃图像的装载时调用的事件句柄。
onerror 在装载图像的过程中发生错误时调用的事件句柄。
onload 当图像装载完毕时调用的事件句柄。

浅谈图片加载

complete属性:图片显示出来之后其属性由false变为true

在这个属性上,IE与其他浏览器有所不同,IE是根据图片的src属性是否是一个有效的图片地址值。即,在IE下,只有当image()对象的src值为一个正确的图片地址时,complete值才为true,否则一直为false,但是在其他浏览器下,当image()对象呗定义完成的时候,其complete值就为true,不会检测其src属性的有效性

也就是说:complete这个属性在IE系浏览器与其他浏览器的判断方式不一样,所以对于适配性来说,最好不用

onload()回调事件

onload()是当图像装载完毕后调用的函数。关键是如何才算是装载完毕,经测试,

  1. 当我们直接在HTML中定义一个标签,并且在其中规定其onload事件,只有当src属性填写正确时,才算是装载完成,否则onload事件一直不触发,直至修改其src值为有效图片地址的时候才算是装载完成
  2. 当我们在JS代码中手动生成一个Image()对象时,无论其src值是否被赋值为有效的图片地址,其onload事件总会触发,即当我们手动生成一个 Image 对象时,一旦其被生成,则认定为装载完成,立即调用onload事件。至于原因,我还没弄明白,看来需要深入的去了解一下浏览器机制,才能明白这一点。

Canvas中悟到的Image对象的用发法

  1. ctx.drawImage(img,x,y)中的Img应当是一个Image对象,可以是从DOM树中获取的Image()子对象,也可以是一个自己定义的Image()对象,不管是这两者中的哪一者,再绘制到画板上时,都必须将绘制语句写到该对象的onload()函数中,其原因在于,Image()的定义是一个异步执行的操作,也就是说,在执行到let img = new Image()时,JS引擎不会等待该语句执行完毕在继续执行,而是直接继续执行,在对象定义完成时,就会自动调用其onlaod.onerror,onabort回调事件。那么问题就是,可能在对象还没有定义完成的时候,已经执行到了ctx.drawImage()这一条语句,所以绘制不出任何效果(而且大部分情况下,JS语句的执行都是快于对象的定义,所以基本上都绘制不出图形)。所以所有的Canvas图形绘制都必须是在onload中进行的。

  2. 问题又出现了,那如果要就行较大型的图形绘制,我不可能把所有代码都写到一个函数里吧,所以再写多个函数时就要多次调用onload事件,但是一个图片的onload事件只能执行一次。所以我想到了一种错误的方法,在不同寒暑表中多次载入同一张图片,并分别调用其回调函数onload,结果当然是失败了。这种情况下onload事件的顺序是无法确定的,即使你是前后分别执行,因为onload函数是异步进行的,现在惟一的办法就是把这张图片统一载入,然后想办法将下面的代码都建立在onlaod函数执行的前提下。然后我找到了一种方法:

    let img = new Image();
    img.addEventListener(‘onload’,’start’,’true’);

    function start(){
    draw1();
    draw2();
    }
    fucntion draw1(){
    ctx.drawImage();

    }

    fucntion draw1(){
    ctx.drawImage();

    }

上面这么写的好处在于,所有的函数后在onload回调函数之后执行了,确保了绘图的正确性。

Last but not least

我在网上搜了很久,在讲解onload()事件时基本就一篇文章,被抄了无数次,但是我试验却发现和文章中的结果完全不一样。文章中写到,即使是在JS代码中定义一个Image()对象时,只要src不赋值,其onload事件就不会触发,但是~像我上面实验的那样,我试验了很多次,都是那样的结果,测试了Chrome,Firefox,IE,Edge,每个浏览器的测试都一样。可能是那篇文章已经是很久之前的了,但是这些同学从未自己试过,直接就抄过去了。所以实践才是真理啊。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :