IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    画一颗圣诞树

    冷石发表于 2022-08-29 09:01:22
    love 0

    JUST FOR FUN

    初始化

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!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>
    1
    2
    3
    4
    5
    6
    7
    8
    canvas {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    background: #fefdfd;
    box-shadow: 0 0 2px rgba(0, 0, 0, 0.4);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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 方法绘制树枝,设置旋转角度绘制左右子树,保存状态,递归绘制子树。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    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()
    }

    branch.png

    画树叶

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    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.png

    画礼物

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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)
    }

    cover

    JUST FOR FUN

    源码



沪ICP备19023445号-2号
友情链接