canvas畫素點獲取 —— 拾色器、放大器

甜蝦發表於2018-09-10

原文地址

前言

最近在學習canvas,然後照葫蘆畫瓢簡單實現了幾個小demo,跟大家一塊學習一下。

主要內容

兩個方法: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 屬性中。

這個樣子:

canvas畫素點獲取 —— 拾色器、放大器

先來一下demo,通過getImageData方法獲取滑鼠指標處的畫素值。

demo1

canvas畫素點獲取 —— 拾色器、放大器

部分程式碼:

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畫素點獲取 —— 拾色器、放大器

但這裡有兩個問題:一個是本地圖片預覽,一個是跨域圖片報錯。

第一個問題之前有寫過一篇文章,可以看這裡,這裡不贅述了。

注意 第二個問題源於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); //轉化成url
        let 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端淘寶頁面產品預覽的效果。這樣:

canvas畫素點獲取 —— 拾色器、放大器

這個效果的實現相當簡單,只是直接利用了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); //轉化成url
            let 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畫素點操作 —— 視訊綠幕摳圖

參考資料

相關文章