canvas教程

React + Canvas 像素风格取色器

字号+ 作者:H5之家 来源:H5之家 2017-12-15 16:04 我要评论( )

有时候我们需要通过图片去获得具体像素的颜色。而强大的 Canvas 为我们提供了现成的接口。 这个功能其实并不难,只不过我们需要

> 综合编程 > React + Canvas 像素风格取色器

React + Canvas 像素风格取色器

综合编程 稀土掘金 12月11日

有时候我们需要通过图片去获得具体像素的颜色。而强大的 Canvas 为我们提供了现成的接口。 这个功能其实并不难,只不过我们需要正确的理解 Canvas 并学会利用它的 API 。 如果你急于看到效果,可以直接访问

演示地

源码地址

我不会详细得写下每一个步骤,但是你可以一边参照源码,一边配合这篇教程进行阅读。

绘制图片(-)

首先,我们需要基于图片去绘制 Canvas。 操作步骤

我们在React中用最小化模型展示出来 我们在 React 的 DidMount 里拿到 image 实例。当然,你也可以直接创建一个 image 对象。

import React, { PureComponent } from 'react' import PropTypes from 'prop-types' export class TestPicker extends PureComponent { static propTypes = { src: PropTypes.string.isRequired, width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, } static defaultProps = { width: 1300, height: 769, src: '/sec3.png' } // 在初始化阶段注册 ref 回调函数去获得 DOM 的实例 constructor (props) { super(props) this.imageCanvasRef = ref => this.imageCanvas = ref this.image = new Image() this.image.src = props.src } // 请注意,一定要在图片加载完全之后才开始绘制 Canvas componentDidMount () { this.image.onload = () => this.renderImageCanvas() } renderImageCanvas = () => { const { width, height } = this.props this.imageCtx = this.imageCanvas.getContext('2d') this.imageCtx.drawImage(this.image, 0, 0, width, height) } render () { const { width, height, src } = this.props return } }

只要将它挂载到相应的节点下,你可以看到有一个和图片一样大小的 Canvas 并且绘制了图片。

但是我们需要注意图片应该是同源的,如果不是同源,Canvas 绘制图片时会报错。具体如何设置可以参考 使用图像 Using images

Canvas 画布与实际宽高

本质上canvas的宽高设定包含两个层面,一个是画布的大小,另外一个则是Canvas 在文档对象所占据的宽高。由于Canvas内部的绘制区域画布大小默认是(width: 300px, height: 150px) ,比如当你 通过 css 设定 (width: 3000px;height: 1500px)的时候,内部的绘制区域大小会被强制与整体宽高保持统一,即内部的绘制区域会被放大十倍。像素级别的放大会导致实际的渲染效果变得更加模糊。因为要注意有时候你的绘制区域出现缩放现象。

实现放大镜位移(二)

我们需要让放大镜的位置在鼠标正中心,并且跟随鼠标移动。 实现方式也比较简单,通过 onmousemove 时获得当前 clientX 和 clientY, 并且减去当前 Canvas 视窗所占据的 left 和 top 即可。

首先,我们在构造函数加了初始化的 state用来表示当前鼠标位移。 在鼠标移动时触发 onmousemove 时去修改 state,通过改变 state 触发 re-render,修改 left 和 top。

constructor() { this.glassCanvasRef = ref => this.glassCanvas = ref this.state = { left: 0, top: 0 } } handleMouseMove = (e) => { // 计算当前鼠标相对 canvas 中的位置 this.calculateCenterPoint({ clientX: e.clientX, clientY: e.clientY }) const { centerX, centerY } = this.centerPoint this.setState({ left: centerX, top: centerY }) } calculateCenterPoint = ({ clientX, clientY }) => { const { left, top } = this.imageCanvas.getBoundingClientRect() this.centerPoint = { centerX: Math.floor(clientX - left), centerY: Math.floor(clientY - top) } } render () { const { width, height, src } = this.props const { left, top } = this.state return } const glassWidth = 100 const glassHeight = 100 绘制放大区域内容(三)

好了,其实我们完成快一半了。接下来就是把放大区域部分的图像放置到我们的放大镜中。 在绘制之前,我们先清除一次画布

handleMouseMove = (e) => { this.glassCtx.clearRect(0, 0, glassWidth, glassWidth) }

我们希望将放大镜部分的元素放大, 我默认取了10倍放大效果。这种情况呈现的样式比较友好,如果你还需要对元素再放大,你只需要修改 scale 即可。

const INIT_NUMBER = 10 const finallyScale = INIT_NUMBER * (scale < 1 ? 1 : scale)

接下来我们使用 canvas 提供的 drawImage 的复杂版本进行截取部分图像。 CanvasRenderingContext2D.drawImage()
根据 MDN 中的演示图片,我们知道

drawImageSmoothingEnable(this.glassCtx, false) this.glassCtx.drawImage(this.image, Math.floor(centerX - (glassWidth / 2) / finallyScale), Math.floor(centerY - (glassHeight / 2) / finallyScale), Math.floor(glassWidth / finallyScale), Math.floor(glassHeight / finallyScale), -INIT_NUMBER, -INIT_NUMBER, glassWidth, glassHeight ) const drawImageSmoothingEnable = (context, enable) => { context.mozImageSmoothingEnabled = enable context.webkitImageSmoothingEnabled = enable context.msImageSmoothingEnabled = enable context.imageSmoothingEnabled = enable }

我们需要计算放大后的因素。此外,由于在计算鼠标当前位置时,可能会有1像素偏差,但被放大了10倍。所以我增加了10个像素的偏移量。你可以根据实际情况来决定偏移。

通过drawImageSmoothingEnable函数让我们最终绘制的图像产生锯齿效果。这样就会有真实的像素风格了。

绘制网格线(四)

关于绘制网格线,依然可以参考 MDN 上的文档。

const GRID_COLOR = 'lightgray' drawGrid(this.glassCtx, GRID_COLOR, INIT_NUMBER, INIT_NUMBER) const drawGrid = (context, color, stepx, stepy) => { context.strokeStyle = color context.lineWidth = 0.5 for (let i = stepx + 0.5; i < context.canvas.width; i += stepx) { context.beginPath() context.moveTo(i, 0) context.lineTo(i, context.canvas.height) context.stroke() } for (let i = stepy + 0.5; i < context.canvas.height; i += stepy) { context.beginPath() context.moveTo(0, i) context.lineTo(context.canvas.width, i) context.stroke() } } 实现取色(五)

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • kaqima.com-a罩杯聚拢-open canvas

    kaqima.com-a罩杯聚拢-open canvas

    2017-12-15 18:00

  • 高德地图和canvas画图结合应用的一些感想(一),高德canvas

    高德地图和canvas画图结合应用的一些感想(一),高德canvas

    2017-12-15 15:03

  • JS+canvas画一个圆锥实例代码

    JS+canvas画一个圆锥实例代码

    2017-12-15 09:42

  • html5使用Canvas绘图的使用方法

    html5使用Canvas绘图的使用方法

    2017-12-14 18:09

网友点评