canvas像素点获取 —— 拾色器、放大器

news/2024/10/21 11:51:35/

原文地址

前言

最近在学习canvas,然后照葫芦画瓢简单实现了几个小demo,跟大家一块学习一下。

主要内容

  • 两个方法:drawImage、getImageData
  • 前端图片预览、跨域图片问题

两个方法:drawImage、getImageData

drawImage

用法:

context.drawImage(img[, sx, sy, swidth, sheight], x, y[, width, height]);
复制代码
参数描述
img可以是图片、视频、画布
sx可选。开始剪切的 x 坐标位置。
sy可选。开始剪切的 y 坐标位置。
swidth可选。被剪切图像的宽度。
sheight可选。被剪切图像的高度。
x在画布上放置图像的 x 坐标位置。
y在画布上放置图像的 y 坐标位置。
width可选。要使用的图像的宽度。(伸展或缩小图像)
height可选。要使用的图像的高度。(伸展或缩小图像)

getImageData

用法:

context.getImageData(x, y, width, height);
复制代码
参数描述
x开始复制的左上角位置的 x 坐标。
y开始复制的左上角位置的 y 坐标。
width将要复制的矩形区域的宽度。
height将要复制的矩形区域的高度。

方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。 是Uint8ClampedArray类型的一维数组,包含着RGBA格式的整型数据。 对于 ImageData 对象中的每个像素,都存在着四方面的信息,即RGBA值:

  • R - 红色 (0-255)
  • G - 绿色 (0-255)
  • B - 蓝色 (0-255)
  • A - alpha 通道 (0-255; 0 是透明的,255 是完全可见的)

color/alpha 以数组形式存在,并存储于 ImageData 对象的 data 属性中。

这个样子:

先来一下demo,通过getImageData方法获取鼠标指针处的像素值。

demo1

部分代码:

methods: {import imgUrl from './component/sample.jpg';export default {data () {return {canvas: null,ctx: null,color: null}},methods: {pick (e, ctx) {let x = e.layerX,y = e.layerY,pixel = ctx.getImageData(x, y, 1, 1),data = pixel.data,rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + ((data[3] / 255).toFixed(2)) + ')';this.color.style.background =  rgba;this.color.textContent = rgba;}},mounted () {this.canvas = this.$refs['canvas'];this.ctx = this.canvas.getContext('2d');this.color = this.$refs['color'];let img = new Image();img.src = imgUrl;img.onload = () => {this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);};this.canvas.onmousemove = () => {this.pick(event, this.ctx);}}
}
复制代码

前端图片预览、跨域图片问题

还可以取本地或者远程跨域的图片,像这样

demo2

但这里有两个问题:一个是本地图片预览,一个是跨域图片报错。

第一个问题之前有写过一篇文章,可以看这里,这里不赘述了。

注意 第二个问题源于canvas无法对没有权限的跨域图片进行操作,如出现跨域,对图片的操作(如getImageData、canvas.toDataURL)会报错:Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. 即canvas已经被跨域的数据污染了。

要解决这个问题,就需要图片所在的服务器允许跨域访问(设置消息头Access-Control-Allow-Origin="*"或者你的网站域名),且本地也需要开启跨域权限(img.crossOrigin = "anonymous")。

由于一般的服务器都是允许跨域的,所以前端只要设置img.crossOrigin = "anonymous"就可以了。

当然,如果服务器设置了图片防盗链的话,我们本地开启了跨域权限也是没有用的。

部分代码:

data () {return {canvas: null,ctx: null,color: null,exterbalUrl: 'http://p8rbt50i2.bkt.clouddn.com/blog/else/miaoWechatIMG241526366731_.pic.jpg'}
},methods: {pick (e, ctx) {let x = e.layerX,y = e.layerY,pixel = ctx.getImageData(x, y, 1, 1),data = pixel.data,rgba = 'rgba(' + data[0] + ',' + data[1] + ',' + data[2] + ',' + (data[3] / 255).toFixed(2) + ')';this.color.style.background =  rgba;this.color.textContent = rgba;},onFileChange (e) {let file = e.target.files[0],blob = new Blob([file]), // 文件转化成二进制文件url = URL.createObjectURL(blob); //转化成urllet img = new Image();img.src = url;img.onload = () => {this.draw(img);URL.revokeObjectURL(url);};},draw (img) {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);},onConfirmUrl () {let img = new Image();//解决跨域问题img.crossOrigin = 'anonymous';img.src = this.exterbalUrl;img.onload = () => {this.draw(img);};}
},mounted () {this.canvas = this.$refs['canvas'];this.ctx = this.canvas.getContext('2d');this.color = this.$refs['color'];this.onConfirmUrl();this.canvas.onmousemove = () => {this.pick(event, this.ctx);}
}
复制代码

demo3

下面是一个放大镜效果,类似PC端淘宝页面产品预览的效果。这样:

这个效果的实现相当简单,只是直接利用了drawImage的“截取”功能,把左侧截取的50 * 50的画布放大后,重新画在了新的画布上。

部分代码:

const SAMPLE_WIDTH = 50,CANVAS_WIDHT = 300;
export default {data () {return {exterbalUrl: 'http://p8rbt50i2.bkt.clouddn.com/blog/else/miaoWechatIMG241526366731_.pic.jpg'}},methods: {pick (e, ctx) {let x = e.layerX,y = e.layerY;if(x < SAMPLE_WIDTH / 2) {x = SAMPLE_WIDTH / 2;}if(x > CANVAS_WIDHT - SAMPLE_WIDTH / 2) {x = CANVAS_WIDHT - SAMPLE_WIDTH / 2;}if(y < SAMPLE_WIDTH / 2) {y = SAMPLE_WIDTH / 2;}if(y > CANVAS_WIDHT - SAMPLE_WIDTH / 2) {y = CANVAS_WIDHT - SAMPLE_WIDTH / 2;}let x1 = x - SAMPLE_WIDTH / 2,y1 = y - SAMPLE_WIDTH / 2;this.drawCanvas(this.img);this.showMagnifier(x1, y1);this.drawSampleFrame(x1, y1);},drawSampleFrame (x1, y1) {this.ctx.fillRect(x1, y1, 50, 50);this.ctx.strokeRect(x1, y1, 50, 50);},onFileChange (e) {let file = e.target.files[0],blob = new Blob([file]), // 文件转化成二进制文件url = URL.createObjectURL(blob); //转化成urllet img = new Image();img.src = url;img.onload = () => {this.img = img;this.drawCanvas(img);URL.revokeObjectURL(url);};},drawCanvas (img) {this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);},onConfirmUrl () {let img = new Image();//解决跨域问题img.crossOrigin = 'anonymous';img.src = this.exterbalUrl;img.onload = () => {this.img = img;this.drawCanvas(img);};},showMagnifier (x, y) {//重点所在this.magnifierCtx.drawImage(this.canvas, x, y, SAMPLE_WIDTH, SAMPLE_WIDTH, 0, 0, this.magnifier.width, this.magnifier.height);}},mounted () {this.canvas = this.$refs['canvas'];this.magnifier = this.$refs['magnifier'];this.ctx = this.canvas.getContext('2d');this.magnifierCtx = this.magnifier.getContext('2d');this.ctx.fillStyle = 'rgba(30, 144, 255, .5)';this.ctx.strokeStyle = '#000';this.onConfirmUrl();this.canvas.onmousemove = () => {this.pick(event, this.ctx);}this.canvas.onmouseout = () => {this.magnifierCtx.clearRect(0, 0, this.magnifier.width, this.magnifier.height);this.drawCanvas(this.img);}}
}
复制代码

另一篇:canvas像素点操作 —— 视频绿幕抠图

参考资料

  • Pixel manipulation with canvas
  • Canvas and images and pixels
  • The Image Embed element

http://www.ppmy.cn/news/345167.html

相关文章

香橙派Orange Pi 3开发板在Armbian系统下使用SPI的方法

香橙派Orange Pi 3开发板&#xff0c;采用全志H6系统级芯片&#xff0c;同时拥有1GB或2GB LPDDR3 内存&#xff0c;可选8GB EMMC Flash。集成了千兆网&#xff0c;MIC&#xff0c;HDMI等功能接口&#xff0c;拥有4个 USB 3.0接口&#xff0c;另外&#xff0c;板子提供了26pin接…

理解视频格式(一)

一、视频 ① 视频是什么&#xff1f; 大家一开口就是视频制作如何如何。究竟什么是视频&#xff1f;音频比较好理解&#xff0c;就是声音&#xff0c;你录声音、做声音就是音频制作。那视频呢&#xff1f;你拍DV叫视频&#xff0c;你做的电脑动画叫视频吗&#xff1f;…

嘉兴桐乡会计考证实操-税务师备考真的那么难吗?

税务师-一学就会&#xff0c;一考就废&#xff0c;税务师备考真的那么难吗&#xff1f; 相信很多学生在备考时都曾遇到过“一听就会&#xff0c;可一做题就废”的情况&#xff0c;明明课上跟着老师做题时很顺利&#xff0c;怎么自己一做就不行呢&#xff1f;这种怪圈怎么破呢&…

基于代码生成器的 J2EE 开发平台 Jeecg-Boot

java自学网 http://www.javaj.cn/thread-451-1-1.html 项目介绍&#xff1a; Jeecg-boot 是一款企业级快速开发平台!采用前后端分离技术:SpringBoot&#xff0c;Mybatis-plus&#xff0c;Shiro&#xff0c;JWT&#xff0c;Vue & Ant Design。提供强大的代码生成器&#xff…

vue学习随笔一

CSS&#xff1a; 层叠样式表&#xff0c;非程序式语言&#xff0c;没有变量、函数的概念&#xff0c;于是有了SASS、LESS等预处理器 SASS&#xff1a;2007诞生&#xff0c;对CSS的扩展提升&#xff0c;增加了变量、函数等特性,比如把CSS中重复的属性值定义成变量去引用它们&…

【分析笔记】全志 i2c-sunxi.c 控制器驱动分析

分析平台&#xff1a;全志 A64 内核版本&#xff1a;Linux 4.9 数据手册&#xff1a;Allwinner_A64_User_Manual_V1.1.pdf (whycan.com) 驱动框架 I2C 设备驱动 作为方案应用来说&#xff0c;我们是最经常要动的地方&#xff0c;这一层主要与具体的芯片功能强关联&#xff0c…

23 H5的spi控制器驱动

spi工作原理参考:http://blog.csdn.net/jklinux/article/details/74287735 在内核里的配置选项: make menuconfig ARCHarm64 CROSS_COMPILEaarch64-linux-gnu-Device Drivers --->[*] SPI support ---><*> Allwinner A31 SPI controller //H5所用的spi控制器…

学习VI编辑器

【PART 1】 简单编辑 &#xff03;移动光标 h j k l &#xff0b; 移动到下一行的行首&#xff0c;直接按回车也可以达到同样效果 &#xff0d; 移动到上一行的行首 一次移动几个位置&#xff0c;例&#xff1a;4l &#xff03;设置页面右边距 不是特别…