阅读 254

画一颗圣诞树🎄

初始化

新建页面,添加一个 canvas 元素,引入 css, js 文件。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Christmas Tree</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <canvas id="tree"></canvas>
    <script src="./index.js"></script>
  </body>
</html>

复制代码
canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  background: #fefdfd;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
}

复制代码
const CANVAS_WIDTH = 360
const CANVAS_HEIGHT = 620

function initCanvas(id) {
  const canvas = document.getElementById(id)
  canvas.width = CANVAS_WIDTH
  canvas.height = CANVAS_HEIGHT
  return canvas
}

function main() {
  const canvas = initCanvas('tree')
}

window.addEventListener('load', main)
复制代码

画树枝

使用 stroke 方法绘制树枝,设置旋转角度绘制左右子树,保存状态,递归绘制子树。

function main() {
  const canvas = initCanvas('tree')
  // 树的起始位置
  const location = [CANVAS_WIDTH * 0.5, CANVAS_HEIGHT]

  drawBranches(canvas, location, 0, 100, 20)
}
/*
 * canvas       画布
 * start        起始位置
 * angle        旋转角度
 * branchHeight 树枝长度
 * branchWidth  树枝宽度
 */
function drawBranches(canvas, start, angle, branchHeight, branchWidth) {
  const ctx = canvas.getContext('2d')
  ctx.save()
  ctx.beginPath()
  // 将画布原点移动到起始位置
  ctx.translate(...start)
  // 设置绘制颜色
  ctx.strokeStyle = '#333'
  // 设置绘制宽度
  ctx.lineWidth = branchWidth
  // 设置旋转角度
  ctx.rotate((angle * Math.PI) / 180)

  ctx.moveTo(0, 0)
  ctx.lineTo(0, -branchHeight)
  ctx.stroke()

  if (branchHeight > 6) {
    // 绘制右子树
    drawBranches(canvas, [0, -branchHeight], 35, branchHeight * 0.5, branchWidth * 0.7)
    // 绘制左子树
    drawBranches(canvas, [0, -branchHeight], -35, branchHeight * 0.5, branchWidth * 0.7)
    // 绘制中间的树干
    drawBranches(canvas, [0, -branchHeight], 0, branchHeight * 0.8, branchWidth * 0.7)
  }

  ctx.restore()
}

复制代码
branches

画树叶

获取画布所有像素点 alpha 通道值,判断此处是否有图像,循环像素点数组绘制半圆。

function main() {
  const canvas = initCanvas('tree')
  const location = [CANVAS_WIDTH * 0.5, CANVAS_HEIGHT]

  drawBranches(canvas, location, 0, 100, 20)
  drawLeaves(canvas)
}

// ...

// 使用一个数组保存绘制树的像素点
const branchPixels = []

function drawLeaves(canvas) {
  const ctx = canvas.getContext('2d')
  // 获取画布像素数据
  const imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
  const data = imageData.data

  for (let y = 0; y < CANVAS_HEIGHT; y++) {
    for (let x = 0; x < CANVAS_WIDTH; x++) {
      // 获取像素点 alpha 通道值
      const alpha = data[4 * (y * CANVAS_WIDTH + x) + 3]

      // 如果 alpha 值大于 0 说明这个位置有图像;排除基础树干的像素点;
      if (alpha > 0 && y < CANVAS_HEIGHT - 100) {
        branchPixels.push([x, y])
      }
    }
  }

  for (let i = 0; i < branchPixels.length; i++) {
    // 减少绘制几率
    if (Math.random() < 0.3) {
      const loc = branchPixels[i]
      loc[0] += (Math.random() - 0.5) * 5
      loc[1] += (Math.random() - 0.5) * 5
      // 设置绘制颜色,越往外颜色越浅
      const green = (255 * (CANVAS_HEIGHT - loc[1])) / CANVAS_HEIGHT

      ctx.save()
      ctx.beginPath()
      ctx.translate(...loc)
      ctx.rotate(Math.random() * Math.PI * 2)
      ctx.fillStyle = `rgba(0, ${green}, 0, .2)`
      // 绘制半圆
      ctx.arc(0, 0, 5, 0, Math.PI)
      ctx.fill()
      ctx.restore()
    }
  }
}
复制代码
leaves

画礼物

使用 fillTextdrawImage 方法绘制文字和图片。

function main() {
  const canvas = initCanvas('tree')
  const location = [CANVAS_WIDTH * 0.5, CANVAS_HEIGHT]

  drawBranches(canvas, location, 0, 100, 20)
  drawLeaves(canvas)
  drawGifts(canvas)
  drawStar(canvas)
}

// ...

const gifts = ['🎁', '🍎', '🍭', '🍬', '🎈', '🧸', '🔔']

function drawGifts(canvas) {
  const ctx = canvas.getContext('2d')

  ctx.save()
  ctx.font = '1.5rem sans-serif'
  for (let i = 0; i < 30; i++) {
    // 从树的像素点数组中随机获取位置
    const location = branchPixels[Math.floor(Math.random() * branchPixels.length)]
    const gift = gifts[i % gifts.length]

    ctx.fillText(gift, ...location)
  }
  ctx.restore()
}

const image = new Image(500, 500)
image.src = 'star.png'

function drawStar(canvas) {
  const size = 50
  const ctx = canvas.getContext('2d')
  const loc = [CANVAS_WIDTH * 0.5 - size / 2, 80]

  // 绘制图片
  ctx.drawImage(image, ...loc, size, size)
}
复制代码

tree

JUST FOR FUN

源码

文章分类
前端
文章标签