【30分钟学完】canvas动画|游戏基础(extra1-1):美图我也行

前言

本文是接续系列教程的extra1,主要是介绍颜色系统在canvas中的应用。
本来是与extra1一起成文的,因为segmentfault莫名其妙的字数限制bug只能分割放送了。

canvas操纵像素

你如果认为canvas只是画图工具,那接下来的操作会颠覆你的认知。canvas提供api可以获取画布上任何一个像素,并可以自由的操作他们。

获取像素

直接访问像素的功能由canvas上下文中的ImageData对象提供,它提供了以下一组方法,都会返回ImageData对象。

  • getImageData()接受x轴坐标、y轴坐标、宽度、高度四个参数,获取画布上这个矩形区域的像素数据;
  • createImageData()可凭空创建指定宽高的矩形区域,初始是黑色,也可以输入一个ImageData对象用于创建一个同样大小的区域,但注意不会复制像素数据
context.getImageData(x, y, width, height);
context.createImageData(width, height);
context.createImageData(anothorImageData);

获取到的ImageData对象中data属性是一个一维数组,乍看乱糟糟的,但细看你会发现其实这就是RGBA的颜色数据,也就是数组中每个四位就是一个像素的颜色数据,这里注意一下透明度A也是0~255,不是CSS里简化过的0~1

    • *

举个例子
现在假定在一个纯红色区域取一块2*2的矩形,我们得到的像素数据是:

let pixels = [255, 0, 0, 255, 255, 0, 0, 255,
              255, 0, 0, 255, 255, 0, 0, 255]

他们与图像的对应关系是从左到右,从上到下,大概就像上面代码格式化这样,如图所示:

根据4对1的对应关系,我们很容易就能写出遍历的办法,offset就相当于指针,每次移动4位,代码如下:

for (let offset = 0, len = pixels.length; offset < len; offset += 4) {
  r = pixels[offset];
  g = pixels[offset + 1];
  b = pixels[offset + 2];
  a = pixels[offset + 3];
}

当需要访问特定坐标的像素时,可以使用如下公式,其中xpos是像素点在该区域的x坐标;ypos是像素点在该区域的y坐标,imagedata.width是指区域横向有多少像素。

let offset = (xpos + ypos * imagedata.width) * 4;
let r = pixels[offset];
let g = pixels[offset + 1];
let b = pixels[offset + 2];
let a = pixels[offset + 3];

绘制像素

可以将修改过的ImageData对象重新用上下文的putImageData()方法绘制到指定区域,该方法接受三个参数:ImageData对象、x轴坐标、y轴坐标。绘制指定的位置绘制ImageData对象的内容。直接看下面例子的核心代码。
先绘制铺满画布的色块,点击按钮触发change事件处理器可改变颜色,过程见注释。
完整例子:演示反色变化

【PS】对js了解不深的朋友可能会有疑问,遍历过程操作的是pixels,imageData怎么会改变呢?
这是因为js中对象都是地址传递的特点,也就是pixels = imageData.data操作只是将pixels变量的指向到imageData.data所指向的内存空间,所以操作pixels就是操作imageData.data。

window.onload = function () {
  const canvas = document.getElementById(‘canvas‘);
  const context = canvas.getContext(‘2d‘);
  // 绘制色块,每个色块宽10像素,高等于画布高,铺满画布
  for (let i = 0; i < canvas.width; i += 10) {
    context.fillStyle = (i % 20 === 0) ? ‘#f00‘ : ((i % 30 === 0) ? ‘#0f0‘ : ‘#00f‘);
    context.fillRect(i, 0, 10, canvas.height);
  }
};

function change() {
  const canvas = document.getElementById(‘canvas‘);
  const context = canvas.getContext(‘2d‘);
  // 获取整个画布的ImageData对象
  const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  // 取出颜色数据
  const pixels = imageData.data;
  // 遍历颜色数据求每个颜色的反色
  for (let offset = 0, len = pixels.length; offset < len; offset += 4) {
    pixels[offset] = 255 - pixels[offset];
    pixels[offset + 1] = 255 - pixels[offset + 1];
    pixels[offset + 2] = 255 - pixels[offset + 2];
    // 这里没有操作透明度
  }
  // 将ImageData重新绘制到画布上
  context.putImageData(imageData, 0, 0);
}

更多有趣的例子

canvas强大的像素操作可以给我们带来更多的可能,也许你会想开始做一个网页版的美图工具了吧(笑)。
这里还有一些有趣的demo可以玩玩:

综合案例

有关颜色的番外部分到这里就基本完结了,最后有个综合题,会应用这些技术。
将系列第二篇中的鼠标画图工具改造成鼠标喷漆工具,这里建议自己动手实践一下。
下面例子基本思路就是取得画布像素数据,每当鼠标点下并移动(执行onMouseMove)就随机改变鼠标周围一定范围的像素点的颜色。
完整案例:鼠标喷漆工具

window.onload = function () {
  const canvas = document.getElementById(‘canvas‘);
  const context = canvas.getContext(‘2d‘);
  // 获得整个画布区域的ImageData对象
  const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  // 取出像素数据
  const pixels = imageData.data;
  // 设定笔刷大小
  const brush_size = 25;
  // 设定笔刷密度
  const brush_density = 80;
  // 笔刷的颜色变量
  let brush_color;

  function onMouseMove() {
    // 根据设定的笔刷密度生成随机像素点
    for (let i = 0; i < brush_density; i++) {
      // 随机像素点角度相对于鼠标的角度
      const angle = Math.random() * Math.PI * 2;
      // 根据设定的笔刷大小,随机像素点以鼠标为圆心的半径
      const radius = Math.random() * brush_size;
      // 计算出像素点的x轴相对坐标
      const xpos = (mouse.x + Math.cos(angle) * radius) | 0;
      // 计算出像素点的y轴相对坐标
      const ypos = (mouse.y + Math.sin(angle) * radius) | 0;
      // 算出该像素点在pixels中的偏移量
      const offset = (xpos + ypos * imageData.width) * 4;
      // 对这个像素点的颜色数据进行操作,将颜色分解成三基色
      pixels[offset] = brush_color >> 16 & 0xff;
      pixels[offset + 1] = brush_color >> 8 & 0xff;
      pixels[offset + 2] = brush_color & 0xff;
      pixels[offset + 3] = 255;
    }
    // 重新绘制区域
    context.putImageData(imageData, 0, 0);
  }
  canvas.addEventListener(‘mousedown‘, () => {
    // 随机一个颜色
    brush_color = utils.parseColor(Math.random() * 0xffffff, true);
    canvas.addEventListener(‘mousemove‘, onMouseMove, false);
  }, false);
  canvas.addEventListener(‘mouseup‘, () => {
    canvas.removeEventListener(‘mousemove‘, onMouseMove, false);
  }, false);
};

原文地址:https://www.cnblogs.com/jlfw/p/11963597.html

时间: 2024-08-01 22:42:52

【30分钟学完】canvas动画|游戏基础(extra1-1):美图我也行的相关文章

【30分钟学完】canvas动画|游戏基础(extra1):颜色那些事

前言 本篇主要讲解关于计算机颜色系统的概念,后续结合一些canvas的应用.因为是"你不知道也没关系"的边缘知识,所以作为本系列教程的扩展,没有兴趣的同学可以跳过. 开始我们万紫千红的故事吧! 本人能力有限,欢迎牛人共同讨论,批评指正. 先从老朋友CSS讲起 我们熟悉的CSS风格颜色表示方式,大体有下面几种,canvas大体是直接沿用这些写法的,但最后包含透明度的写法有些许不同. #RRGGBB:十六进制格式,红绿蓝分别用两位十六进制数表示. #RGB:简写的十六进制格式,转换成6位格

【30分钟学完】canvas动画|游戏基础(2):从零开始画画

前言 上篇主要是理论的概述,本篇会多些实践,来讲讲canvas的基础用法,并包含一些基础三角函数的应用,推荐没有canvas基础的朋友阅读,熟悉的朋友可以跳过. 本人能力有限,欢迎牛人共同讨论,批评指正. 一起来画画吧 canvas的API有很多,如果一一列举30分钟你是绝对看不完的,而且怎么流水账还不如自己去看文档呢(笑),本教程的思路是用实例一步一步从无到有讲解基础用法. canvas相关文档 准备工作 布置画布:通过添加<canvas>标签,添加canvas元素: 获取画布:通过<

【30分钟学完】canvas动画|游戏基础(4):边界与碰撞

前言 本系列前几篇中常出现物体跑到画布外的情况,本篇就是为了解决这个问题. 阅读本篇前请先打好前面的基础. 本人能力有限,欢迎牛人共同讨论,批评指正. 越界检测 假定物体是个圆形,如图其圆心坐标即是物体的x轴和y轴坐标. 越界是常见的场景,一般会有两种场景的越界:一是整个物体移出区域,二是物体接触到区域边界.我们以画布边界为例进行讨论,示例中矩形边界即是: let top = 0; let bottom = canvas.height; let left = 0; let right = can

30分钟学玩转RabbitMQ

最近在学习RabbitMQ,在网上找了不少资料发现都特高端.动辄集群部署,分布式架构什么的,对于一个初学者实在不够友好.心想求人不如求自己,为什么不自己整理一套资料呢?于是<30分钟学玩转RabbitMQ>诞生. 一.准备工作 据说RabbitMQ是可以部署到Windows环境的,不过作为一个专业级的开发人员怎么能够让这样的事情发生呢?自然我们的准备工作从Linux开始.首先在虚拟机中安装CentOS 7,选择英文,最小安装,默认开启网络以及创建一个root用户: 完成以后进入系统,由于最小安

每天坚持30分钟学python——python基础

变量 在使用变量之前,必须先赋值 变量名可以包含字母.数字.下划线,但变量名不能以数字开头 大小写敏感 等号即为赋值 >>> first=3 >>> second=8 >>> three=first+second >>> print(three) 字符串 加上’’或者""即为字符串 在字符串中出现单引号或者双引号需要用\转义 转义字符\可以转义很多字符,比如\n表示换行,\t表示制表符,字符\本身也要转义,所以\\

30分钟玩转css3动画, transition,animation

其实css3动画是就是2种实现,一种是transition,另一种就是animation.transition实现的话就是只能定制开始帧,和结束2帧:而animation实现的话可以写很多关键帧.没有前戏,直进主题. transition 包含4个值,例如:-webkit-transition:all .5s ease 1s;  分别顺序是(m代表必须): 变换的属性(m).变换过渡的时间(m).变换的速率.变换延时执行的时间.来个小demo: <!DOCTYPE html> <html

30分钟,学会经典小游戏编程!

在80后.90后的儿时记忆里,俄罗斯方块是必备的消遣小游戏,它的玩法非常简单基本大家都懂,但如何用编程语言开发一款儿时同款「俄罗斯方块」,恐怕知道的同学就很少啦. 位置掩码和旋转掩码 俄罗斯方块游戏中的格子一般是10列20行(10*20),我们称之为世界地图. 一般都是这种竖屏的界面 10*20的空间是用来盛放方块的,当方块落定之后位置便不能再改变.这个时候它会被保存到地图的状态中,我们给地图状态设计一个二维的数组. 方块有7种样式组成,最大的长宽是4个方格,为了在逻辑上比较好的处理所有类型的方

每天坚持30分钟学python

Python介绍 应用领域:Web开发.数据库编程.桌面GUI应用开发.科学与数值计算.教育教学.网络应用开发.软件开发(控制.管理.测试等).游戏与3D影像:游戏开发.网站建设.机器人和航天飞机控制等领域,Python几乎可以用在任何场合. 版本:目前,Python有两个版本,一个是2.x版,一个是3.x版,这两个版本是不兼容的.本教程采用Python 3.4.1. Python是跨平台的,它可以运行在Windows.Mac和各种Linux/Unix系统上.在Windows上写Python程序

(再转)30分钟学STL

msflexgrid%E7%9A%84%E7%BC%96%E8%BE%91 http://index.ttplayer.com/songlist/502549479 http://list.mp3.baidu.com/songlist/502170769 http://zhangmen.baidu.cn/songlist/502681933 http://list.mp3.baidu.com/songlist/502684741 http://zhangmen.baidu.cn/songlist