文章目录
前端处理图片数据,有提供几个常用的API,如Image、ImageData、ImageBitmap等等。这些对象可以为我们操作图片带来较大帮助,今天我们就详细介绍下这几个有用的对象接口。
前端处理图片,首先能想到的API就是 Image 对象。
它的主要作用是:能够加载一张图片资源,创建并返回一个新的 HTMLImageElement 实例,拥有图像元素的所有属性和事件。
构造函数语法:Image(width, height),带有两个参数,表示图片的宽度和高度属性。
使用它也非常简单,如下所示:
const img = new Image()
img.src = 'chrome.png'
以上代码定义一个img的对象实例,并给src属性赋值一个图片链接,这样就能加载该图片资源,得到一个图片元素对象img,接下来就能读取图片的width和height等宽高元素了。
Image对象实例还有一些常用的属性和事件,也是我们必须要了解的:
Image对图片资源的加载是一种异步的方式,一般是通过 onload 事件监听,实时获取到图片对象实例,如下使用 Promise 对象进行函数封装:
const loadImage = (url) => {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => {
resolve(img)
}
img.onerror = (err) => {
reject(err)
}
img.src = url
})
}
以上代码,就是最常使用Image的方式,我们以前关于图片应用的博文也有提到该种用法。在之前的博文一步步实现前端图片裁剪功能,就是使用的这种方式加载图像资源。
src属性,可以的取值:
这几种加载图像资源的方式,在当前的前端开发中,都是非常常见的。其中,关于Object-URL和Base64的知识,可查看之前的博文:前端二进制知识与相关API 和 Base64知识详解。
需要注意的是,Image对象是Web-API,依赖浏览器环境;不是JS-API,在nodejs中无法使用。
由上面已知,Image构造函数会返回一个 HTMLImageElement 实例,该实例加载到页面上,就对应了一个 <img> 标签元素。通过下面两种方式,可以获取或生成新的img元素实例。
<img src="..." id="imgElm">
const img = document.getElementById('imgElm')
console.log(img.width)
以上代码,页面html定义了一个img元素,可以在JS中通过元素id获取到对应的元素对象。
const img = document.createElement('img')
img.onload = () => {
document.body.append(img)
}
img.src = './logo.png'
以上代码,使用 document.createElement() 方法新建一个图片对象,设置图像资源,加载完成后显示在页面上。
可以看出,使用 document.createElement() 方法和使用 Image() 的方式几乎是一致的,所能达成的效果也相似,它们都返回了一个新的 HTMLImageElement 对象实例。
通过简单的测试对比,单张或少量图片的加载性能上,
Image()和createElement()几乎没区别,都可以使用;但在大批量加载图片资源时,Image()比createElement()稍微快一些。
前端常用图片处理中,<img> 标签用于加载图片资源,但在各种图片操作上稍显不足,所以一般会使用到 canvas 画布,接下来的两个对象,都是在 canvas 环境下使用的。
ImageData 对象表示 canvas 元素指定区域的像素数据。
图片像素数据实际上就是一个个的颜色值,可使用 RGBA 颜色模型来表示,所以 ImageData 对象的像素数据,就是图像的一个个像素点的颜色值,长度为 windth * height * 4,这里的 4 就是对应的 RGBA 4个颜色通道。图像的详细知识可见博文 图片基础知识介绍。
通过构造函数,可以很方便的创建对象实例:
new ImageData(array, width, height);
new ImageData(width, height);
参数说明
Uint8ClampedArray 类型数组的实例,存储的是像素点的颜色值数据,数组元素按每个像素点的 RGBA 4通道的数值依次排列,该数组的长度必须为 windth * height * 4,否则报错。ImageData 对象实例属性,有三个: data、width、height。
const imgData = new ImageData(512, 512)
console.log(imgData)
// ImageData {data: Uint8ClampedArray(1048576), width: 512, height: 512, colorSpace: 'srgb'}
// data: Uint8ClampedArray(1048576) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …]
以上代码,定义了一个 512 * 512 宽高的ImageData对象实例,通过输出,可以看到,像素点数据默认值都为0。
colorSpace:色彩空间,可取两个值
srgb或display-p3,都是RGB颜色模型的色彩空间,部分浏览器不支持。
关于 RGB 颜色的详细知识,可见博文 前端需要了解的颜色模型,RGB、HSL和HSV。
ImageData对象的数据参数和属性,是 Uint8ClampedArray 类型数组的实例,关于 Uint8ClampedArray,我们也需有所了解。
Uint8ClampedArray 是8位无符号整型固定数组,属于11个类型化数组(TypeArray)中的一个,元素值固定在0-255区间的。
它的属性参数及用法,与其他中的类型化数组一样,具体的介绍可见博文 前端二进制知识与相关API 。
因为 Uint8ClampedArray 无符号整形固定的特点,对于存储像素点的颜色值正好,RGBA 四个通道,每个通道的取值都是 0 - 255 间的数字。如表示红色、无透明,[255, 0, 0, 255]。
注意:
0 - 1 区间的值,包括在canvas中颜色属性也是 0 - 1。而 RGB 三色的取值则基本保持一致,即 0 - 255。宽 * 高 * 4 。ImageData 图像像素点对象,是基于前端的canvas环境,应用也都是在canvas操作中,常见的如创建方法 createImageData()、读取方法 getImageData()、更新方法 putImageData()。
这三个方法都是canvas的绘制上下文的实例方法,下面我们一一介绍。
createImageData() 用于创建一个全新的空的ImageData对象,与 ImageData() 构造函数作用一样,返回像素点信息数据。
基本语法:
context.createImageData(width, height)
context.createImageData(imagedata)
通过宽高、或者其他ImageData对象进行创建新的对象。
返回canvas画布中部分或全部的像素点信息数据。
基础语法:
context.getImageData(sx, sy, sWidth, sHeight)
参数说明:
sx:返回图像的起始横坐标
sy:返回图像的起始纵坐标
sWidth:返回的图像的宽度
sHeight:返回图像的高度
将指定的 ImageData 对象像素点数据绘制到位图上。
基础语法:
context.putImageData(imagedata, dx, dy [, dirtyX, [ dirtyY, [ dirtyWidth, [dirtyHeight]]]]);
参数说明:
imagedata:图像像素点信息
dx:在目标canvas中的起始横坐标
dy:在目标canvas中的起始纵坐标
dirtyX:图像数据渲染区域的左上角横坐标,可选
dirtyY:图像数据渲染区域的左上角纵坐标,可选
dirtyWidth:图像数据渲染区域的宽度,可选
dirtyHeight:图像数据渲染区域的高度,可选
其中,getImageData() 和 putImageData() 是前端通过canvas对图像进行像素级别处理的必不可少的两个方法。
下面,我们看一个具体的示例。
首先,我们创建一个随机函数用于取颜色值,定义宽高100*100的canvas元素:
const randomRGB = () => Math.floor(Math.random() * (255 - 0) + 0)
const canvas = document.createElement('canvas')
canvas.width = 100
canvas.height = 100
document.body.append(canvas)
接着,我们通过 createImageData() 定义一个ImageData像素点对象实例,并给它的元素赋值,R通道默认255,BG通道取随机值,然后使用 putImageData() 将像素点数据绘制出来:
const ctx = canvas.getContext('2d')
const imagedata = ctx.createImageData(100, 100)
const length = imagedata.data.length
for (let i = 0; i < length; i += 4) {
imagedata.data[i] = 255
imagedata.data[i + 1] = randomInRange()
imagedata.data[i + 2] = randomInRange()
imagedata.data[i + 3] = 255
}
ctx.putImageData(imagedata, 0, 0)
最后,我们通过一个1s的定时器,使用 getImageData() 获取像素点数据后,将颜色值的R通道改为0,再重新绘制图像:
setTimeout(() => {
const imgData = ctx.getImageData(0, 0, 100, 100)
const len = imgData.data.length
for (let i = 0; i < len; i += 4) {
imgData.data[i] = 0
}
ctx.putImageData(imgData, 0, 0)
}, 1000)
以上代码,就是展示了像素点操作的基本使用方法,会有一个图像改变的过程,从红色系变到绿色系,见下图所示:

除了 ImageData 以外,在canvas中还有另外一个对象,也可用于处理图像数据,那就是 ImageBitmap。
ImageBitmap 表示一个位图图像,可绘制到canvas中,并且具有低延迟的特性。
与 ImageData 一样的是,他们都是在浏览器环境下可以访问的全局对象。
与 ImageData 不一样的是,ImageBitmap 没有构造函数,可以直接引用对象(无意义),但无法通过构造函数创建,而需要借助 createImageBitmap() 进行创建。
// 直接引用对象,正常输出
ImageBitmap
// ƒ ImageBitmap() { [native code] }
// 函数调用,报错:非法构造函数
ImageBitmap()
// Uncaught TypeError: Illegal constructor
属性和方法:
createImageBitmap() 接受不同的图像资源,返回一个成功结果为ImageBitmap的Promise异步对象。
基本语法如下:
createImageBitmap(image[, options])
createImageBitmap(image, sx, sy, sw, sh[, options])
基本参数
image:图像源
可取值:img, SVG-image, video, canvas, HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, Blob, File, ImageData, ImageBitmap, 或 OffscreenCanvas 对象
sx:裁剪起点横坐标
sy:裁剪起点纵坐标
sw:裁剪宽度
sh:裁剪高度
options:可选,为其设置选项的对象。可用的选项是:imageOrientation: 是原样呈现还是垂直翻转,可选 none(默认)、flipY
premultiplyAlpha: 颜色通道是否由alpha通道预乘,可选 none、premultiply、default(默认)
colorSpaceConversion: 是否使用色彩空间转换进行解码,可选 none、default (默认)
resizeWidth: 压缩新宽度
resizeHeight: 压缩新高度
resizeQuality: 压缩质量,可选 pixelated、low(默认)、medium、high
createImageBitmap可以直接读取多种图像数据源,比如 ImageData、File、以及多种HTML元素对象等等,这可以让我们更加灵活的处理图像数据。
下面看一个读取File对象的示例。
ImageBitmap 对象主要用于在canvas中,使用 drawImage() 接口进行图像绘制时,作为该接口的参数(图像源):
<input id="input-file" type="file" accept="image/*" multiple />
以上代码,先定义一个input文件上传控件。
document.getElementById('input-file').onchange = (e) => {
const file = e.target.files[0]
createImageBitmap(file).then(imageBitmap => {
const canvas = document.createElement('canvas')
canvas.width = imageBitmap.width
canvas.height = imageBitmap.height
const ctx = canvas.getContext('2d')
ctx.drawImage(imageBitmap, 0, 0)
document.body.append(canvas)
})
}
通过监听控件,读取到文件流对象,createImageBitmap() 可以直接读取文件流数据,生成一个ImageBitmap位图对象。创建canvas画布对象,使用 drawImage() 加载该imageBitmap位图对象,即可将文件直接绘制到画布中,展示出来。
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss