JS 3D模型引擎ZDog VUE应用

2 篇文章 0 订阅

突然发现了一个特别有意思的网站,记录一下:https://zzz.dog

免费开源!使用简单、超轻量的 javacript 3D 模型引擎 - Zdog.js

Zdog.js (下称Zdog)是一款基于和 SVG 的 JavaScript 3D 引擎,通过简单的 api 可以让我们在 Web 页面上设计和渲染简单的 3D 模型。Zdog 是一个伪 3D 引擎。其几何形状存在于3D空间中,但被渲染为平面形状。这使得 Zdog 很特别。
在这里插入图片描述
Node包下载:
npm install zdog

Loading.vue

<template>
    <div>
        <div class="continer">
            <canvas class="illo"></canvas>
        </div>
    </div>
</template>

<script>
import animate from '../castle.js'

export default {
    name: 'Loading',
    mounted() {
        animate()
    }
}
</script>

<style scoped>
.continer{
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}
.illo {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    height: 220px !important;
    width: 220px !important;
    margin: auto;
    cursor: move;
    z-index: 6;
}
</style>

castle.js

// -------------------------- demo -------------------------- //
import Zdog from 'zdog'

export default function() {
    // Made with Zdog

    // colors
    const red = '#F44'
    const navy = '#247'
    const blue = '#5AE'
    const gold = '#FB3'
    const white = 'white'

    // -------------------------- makeBuilding -------------------------- //

    function makeBuilding(options) {
        const wallX = options.width / 2
        const wallY = options.height
        const wallZ = options.depth / 2

        // collect walls
        const building = {};

        // south/noth walls
        [true, false].forEach(function(isSouth) {
            const wallTZ = isSouth ? -wallZ : wallZ
            const wallGroup = new Zdog.Group({
                addTo: options.addTo,
                translate: { z: -wallTZ }
            })

            let wallPath = [
                { x: -wallX, y: -wallY }
            ]

            if (options.gable == 'ns') {
                wallPath.push({ x: 0, y: -wallY - wallX })
            }

            wallPath = wallPath.concat([
                { x: wallX, y: -wallY },
                { x: wallX, y: 0 },
                { x: -wallX, y: 0 }
            ])

            // wall
            new Zdog.Shape({
                path: wallPath,
                addTo: wallGroup,
                color: isSouth ? red : gold
            })

            const windowColor = isSouth ? navy : red
            const windowProperty = isSouth ? 'southWindows' : 'northWindows'
            handleWindows(options, windowProperty, wallGroup, windowColor)

            const wallProperty = isSouth ? 'southWall' : 'northWall'
            building[wallProperty] = wallGroup
        });

        // east/west wall
        [true, false].forEach(function(isWest) {
            const wallGroup = new Zdog.Group({
                addTo: options.addTo,
                translate: { x: isWest ? -wallX : wallX },
                rotate: { y: TAU / 4 }
            })

            let wallPath = [
                { x: -wallZ, y: -wallY }
            ]

            if (options.gable == 'ew') {
                wallPath.push({ x: 0, y: -wallY - wallZ })
            }

            wallPath = wallPath.concat([
                { x: wallZ, y: -wallY },
                { x: wallZ, y: 0 },
                { x: -wallZ, y: 0 }
            ])

            // wall
            new Zdog.Shape({
                path: wallPath,
                addTo: wallGroup,
                color: isWest ? blue : white
            })

            const windowColor = isWest ? navy : blue
            const windowProperty = isWest ? 'westWindows' : 'eastWindows'
            handleWindows(options, windowProperty, wallGroup, windowColor)

            const wallProperty = isWest ? 'westWall' : 'eastWall'
            building[wallProperty] = wallGroup
        })

        const roofMakers = {
            ns: function() {
                const y0 = -wallY - wallX
                const roofPanel = new Zdog.Shape({
                    path: [
                        { x: 0, y: y0, z: wallZ },
                        { x: 0, y: y0, z: -wallZ },
                        { x: wallX, y: -wallY, z: -wallZ },
                        { x: wallX, y: -wallY, z: wallZ }
                    ],
                    addTo: options.addTo,
                    color: gold
                })
                roofPanel.copy({
                    scale: { x: -1 },
                    color: navy
                })
            },

            ew: function() {
                const y0 = -wallY - wallZ
                const xA = options.isChurch ? -wallX + 8 : -wallX
                const roofPanel = new Zdog.Shape({
                    path: [
                        { z: 0, y: y0, x: xA },
                        { z: 0, y: y0, x: wallX },
                        { z: -wallZ, y: -wallY, x: wallX },
                        { z: -wallZ, y: -wallY, x: xA }
                    ],
                    addTo: options.addTo,
                    color: red
                })
                roofPanel.copy({
                    path: [
                        { z: 0, y: y0, x: -wallX },
                        { z: 0, y: y0, x: wallX },
                        { z: -wallZ, y: -wallY, x: wallX },
                        { z: -wallZ, y: -wallY, x: -wallX }
                    ],
                    scale: { z: -1 },
                    color: navy
                })
            }
        }

        const roofMaker = roofMakers[options.gable]
        if (roofMaker) {
            roofMaker()
        }

        return building
    }

    function handleWindows(options, windowProperty, wallGroup, color) {
        const windowOption = options[windowProperty]
        if (!windowOption) {
            return
        }

        const columns = windowOption[0]
        const rows = windowOption[1]
        // let windowPaths = [];
        for (let row = 0; row < rows; row++) {
            for (let col = 0; col < columns; col++) {
                const x = (col - (columns - 1) / 2) * 6
                const y = -options.height + (row + 0.75) * 8
                const windowPath = [
                    { x: x + -1, y: y + -2 },
                    { x: x + 1, y: y + -2 },
                    { x: x + 1, y: y + 2 },
                    { x: x + -1, y: y + 2 }
                ]
                new Zdog.Shape({
                    path: windowPath,
                    addTo: wallGroup,
                    color: color
                })
            }
        }
    }

    // -------------------------- lilPyramid -------------------------- //

    function lilPyramid(options) {
        const anchor = new Zdog.Anchor({
            addTo: options.addTo,
            translate: options.translate
        })

        const panel = new Zdog.Shape({
            path: [
                { x: 0, y: -3, z: 0 },
                { x: 3, y: 0, z: 0 },
                { x: 0, y: 0, z: 3 }
            ],
            addTo: anchor,
            color: red
        })

        panel.copy({
            rotate: { y: TAU / 4 },
            color: red
        })
        panel.copy({
            rotate: { y: TAU / 2 },
            color: navy
        })
        panel.copy({
            rotate: { y: TAU * 3 / 4 },
            color: navy
        })
    }

    function hedge(options) {
        const anchor = new Zdog.Anchor({
            addTo: options.addTo,
            translate: options.translate
        })

        const ball = new Zdog.Shape({
            path: [{ y: 0 }, { y: -1 }],
            addTo: anchor,
            translate: { y: -2.5 },
            stroke: 5,
            color: options.color || navy
        })

        ball.copy({
            stroke: 4,
            translate: { y: -5 }
        })

        ball.copy({
            stroke: 2.5,
            translate: { y: -7.5 }
        })
    }

    // -------------------------- demo -------------------------- //

    const illoElem = document.querySelector('.illo')
    const w = 160
    const h = 160
    const minWindowSize = Math.min(window.innerWidth, window.innerHeight)
    const zoom = Math.min(6, Math.floor(minWindowSize / w))
    illoElem.setAttribute('width', w * zoom)
    illoElem.setAttribute('height', h * zoom)

    let isSpinning = true
    const TAU = Zdog.TAU

    const illo = new Zdog.Illustration({
        element: illoElem,
        zoom: zoom,
        rotate: { y: TAU / 8 },
        dragRotate: true,
        onDragStart: function() {
            isSpinning = false
        }
    });

    // default to flat, filled shapes
    [Zdog.Shape, Zdog.Rect, Zdog.Ellipse].forEach(function(ItemClass) {
        ItemClass.defaults.fill = true
        ItemClass.defaults.stroke = false
    })

    // -- illustration shapes --- //

    const quarterView = 1 / Math.sin(TAU / 8)

    // anchor
    const town = new Zdog.Group({
        addTo: illo,
        translate: { y: 36 },
        scale: { x: quarterView, z: quarterView },
        updateSort: true
    })

    // ----- front building ----- //

    const frontAnchor = new Zdog.Anchor({
        addTo: town,
        translate: { x: 16, y: -4, z: 20 }
    })

    const frontBuilding = makeBuilding({
        width: 22,
        depth: 16,
        height: 20,
        addTo: frontAnchor,
        gable: 'ew',
        southWindows: [3, 1],
        eastWindows: [2, 2],
        westWindows: [2, 2],
        northWindows: [3, 2]
    })

    // east gable dot
    const gableDot = new Zdog.Ellipse({
        diameter: 2,
        addTo: frontBuilding.eastWall,
        color: blue,
        translate: { y: -20 }
    })
    // west gable dot
    gableDot.copy({
        addTo: frontBuilding.westWall,
        color: navy
    })

    // south doors
    const door = new Zdog.Shape({
        path: [
            { x: -2.5, y: 0 },
            { x: -2.5, y: -5.5 },
            {
                arc: [
                    { x: -2.5, y: -8 },
                    { x: 0, y: -8 }
                ]
            },
            {
                arc: [
                    { x: 2.5, y: -8 },
                    { x: 2.5, y: -5.5 }
                ]
            },
            { x: 2.5, y: 0 }
        ],
        addTo: frontBuilding.southWall,
        translate: { x: -4.5 },
        color: navy
    })
    door.copy({
        translate: { x: 4.5 }
    });

    [-1, 1].forEach(function(zSide) {
        const frontGableGroup = new Zdog.Group({
            addTo: frontAnchor,
            translate: { y: -20, z: -8 * zSide }
        })

        // front building gable
        new Zdog.Shape({
            path: [
                { x: 0, y: -6 },
                { x: -6, y: 0 },
                { x: 6, y: 0 }
            ],
            addTo: frontGableGroup,
            translate: { y: 1 },
            color: zSide == -1 ? red : gold
        })

        gableDot.copy({
            addTo: frontGableGroup,
            translate: { y: -2 },
            color: zSide == -1 ? navy : red
        })

        const frontGableSide = new Zdog.Shape({
            path: [
                { x: 0, y: 0, z: 0 },
                { x: 5, y: 5, z: 0 },
                { x: 0, y: 0, z: 5 * zSide }
            ],
            addTo: frontAnchor,
            translate: { y: -25, z: -8 * zSide },
            color: gold
        })
        frontGableSide.copy({
            scale: { x: -1 },
            color: navy
        })
    })

    // ----- left building ----- //

    const leftAnchor = new Zdog.Anchor({
        addTo: town,
        translate: { x: -13, y: -10, z: 23 }
    })

    const leftBuilding = makeBuilding({
        width: 16,
        depth: 22,
        height: 20,
        addTo: leftAnchor,
        gable: 'ns',
        southWindows: [2, 2],
        eastWindows: [3, 2],
        westWindows: [3, 1],
        northWindows: [2, 2]
    })

    door.copy({
        addTo: leftBuilding.westWall,
        translate: { x: -4.5 }
    })
    door.copy({
        addTo: leftBuilding.westWall,
        translate: { x: 4.5 }
    })

    // ----- cupola ----- //

    const cupolaNSPanel = new Zdog.Shape({
        path: [
            { x: -1, y: 0 },
            { x: 3, y: 0 },
            { x: 3, y: 9 },
            { x: -1, y: 5 },
            // HACK add point to sort in front of roof
            { move: { x: 8, z: 4 } }
        ],
        addTo: leftAnchor,
        translate: { y: -34, z: 3 },
        color: red
    })
    cupolaNSPanel.copy({
        scale: { x: -1 }
    })
    cupolaNSPanel.copy({
        scale: { z: -1 },
        translate: { y: -34, z: -3 },
        color: gold
    })
    cupolaNSPanel.copy({
        translate: { y: -34, z: -3 },
        scale: { x: -1, z: -1 },
        color: gold
    });

    [-1, 1].forEach(function(xSide) {
        const group = new Zdog.Group({
            addTo: leftAnchor,
            translate: { y: -34, x: 3 * xSide }
        })
        // ew panel
        new Zdog.Shape({
            path: [
                { z: 3, y: 0 },
                { z: 0, y: -3 },
                { z: -3, y: 0 },
                { z: -3, y: 9 },
                { z: 3, y: 9 },
                // HACK add point to sort in front of roof
                { move: { x: 16 * xSide }}
            ],
            addTo: group,
            color: xSide == -1 ? blue : white
        })
        gableDot.copy({
            addTo: group,
            translate: { y: 3 },
            rotate: { y: TAU / 4 },
            color: xSide == -1 ? navy : blue
        })
    })

    // cupola roof panel
    const cupolaRoofPanel = new Zdog.Shape({
        path: [
            { x: -3, y: -3, z: 0 },
            { x: 3, y: -3, z: 0 },
            { x: 3, y: 0, z: 3 },
            { x: -3, y: 0, z: 3 }
        ],
        addTo: leftAnchor,
        translate: { y: -34 },
        color: navy
    })
    cupolaRoofPanel.copy({
        scale: { z: -1 },
        color: red
    })

    // ----- left building slopes ----- //

    // east slope
    const leftEWSlope = new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 11 },
            { x: 0, y: 0, z: -11 },
            { x: 6, y: 6, z: -11 },
            { x: 6, y: 6, z: 11 }
        ],
        addTo: leftAnchor,
        translate: { x: 8 },
        color: gold
    })
    // west slope
    leftEWSlope.copy({
        scale: { x: -1 },
        translate: { x: -8 },
        color: gold
    })

    // south slope
    new Zdog.Shape({
        path: [
            { z: 0, y: 0, x: -8 },
            { z: 0, y: 0, x: 8 },
            { z: 6, y: 6, x: 8 },
            { z: 6, y: 6, x: -8 }
        ],
        addTo: leftAnchor,
        translate: { z: 11 },
        color: navy
    })

    // south east corner
    const leftCorner = new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 0 },
            { x: 6, y: 6, z: 0 },
            { x: 0, y: 6, z: 6 }
        ],
        addTo: leftAnchor,
        translate: { x: 8, z: 11 },
        color: red
    })
    // south west corner
    leftCorner.copy({
        scale: { x: -1 },
        translate: { x: -8, z: 11 },
        color: blue
    })

    // ----- back tower ----- //

    const towerAnchor = new Zdog.Anchor({
        addTo: town,
        translate: { x: -13, y: -24, z: -4 }
    })

    const tower = makeBuilding({
        width: 16,
        depth: 16,
        height: 28,
        addTo: towerAnchor,
        gable: 'ns',
        southWindows: [2, 3],
        eastWindows: [2, 2],
        westWindows: [2, 3],
        northWindows: [2, 3]
    })

    door.copy({
        addTo: tower.eastWall,
        translate: { x: 0 },
        color: blue
    })

    gableDot.copy({
        addTo: tower.southWall,
        translate: { y: -29 },
        color: navy
    })

    gableDot.copy({
        addTo: tower.northWall,
        translate: { y: -29 },
        color: red
    })

    const towerChimney = new Zdog.Shape({
        addTo: towerAnchor,
        path: [{ y: 0 }, { y: 4 }],
        translate: { x: -2, y: -37, z: 1 },
        stroke: 2,
        color: navy
    })
    towerChimney.copy({
        translate: { x: -2, y: -37, z: -3 }
    })

    // plume
    new Zdog.Shape({
        path: [
            { x: -3, y: 1 },
            {
                arc: [
                    { x: -3, y: -1 },
                    { x: -1, y: -1 }
                ]
            },
            { x: 3, y: -1 },
            {
                arc: [
                    { x: 3, y: 1 },
                    { x: 1, y: 1 }
                ]
            }
        ],
        addTo: towerAnchor,
        translate: { x: -2, y: -42, z: -6 },
        rotate: { y: -TAU / 4 },
        stroke: 2,
        color: blue
    })

    // ----- tower slopes ----- //

    // big east slope
    const towerEWSlope = new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 1 },
            { x: 0, y: 0, z: -1 },
            { x: 1, y: 1, z: -1 },
            { x: 1, y: 1, z: 1 }
        ],
        addTo: towerAnchor,
        translate: { x: 8 },
        // size by scaling
        scale: { x: 20, y: 20, z: 8 },
        color: gold
    })

    // south slope down to left building
    const towerNSSLope = new Zdog.Shape({
        path: [
            { z: 0, y: 0, x: 1 },
            { z: 0, y: 0, x: -1 },
            { z: 1, y: 1, x: -1 },
            { z: 1, y: 1, x: 1 }
        ],
        addTo: towerAnchor,
        translate: { z: 8 },
        scale: { x: 8, y: 14, z: 8 },
        color: navy
    })

    // south east corner
    new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 0 },
            { x: 20, y: 20, z: 0 },
            { x: 6, y: 20, z: 8 },
            { x: 0, y: 14, z: 8 }
        ],
        addTo: towerAnchor,
        translate: { x: 8, z: 8 },
        color: red
    })

    // north slope
    towerNSSLope.copy({
        translate: { z: -8 },
        scale: { x: 8, y: 20, z: -7 },
        color: gold
    })

    // north east corner
    new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 0 },
            { x: 20, y: 20, z: 0 },
            { x: 0, y: 20, z: -7 }
        ],
        addTo: towerAnchor,
        translate: { x: 8, z: -8 },
        color: gold
    })

    // west slope
    towerEWSlope.copy({
        scale: { x: -12, y: 20, z: 8 },
        translate: { x: -8 },
        color: gold
    })

    // north west corner
    new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 0 },
            { x: -12, y: 20, z: 0 },
            { x: 0, y: 20, z: -7 }
        ],
        addTo: towerAnchor,
        translate: { x: -8, z: -8 },
        color: red
    })

    // south west corner back to left building
    new Zdog.Shape({
        path: [
            { x: 0, y: 0, z: 0 },
            { x: -12, y: 20, z: 0 },
            { x: -6, y: 20, z: 8 },
            { x: 0, y: 14, z: 8 }
        ],
        addTo: towerAnchor,
        translate: { x: -8, z: 8 },
        color: blue
    })

    // ----- church ----- //

    const churchAnchor = new Zdog.Anchor({
        addTo: town,
        translate: { x: -5, y: -4, z: -27 }
    })

    const church = makeBuilding({
        isChurch: true, // special flag for roof
        width: 22,
        depth: 16,
        height: 28,
        addTo: churchAnchor,
        gable: 'ew',
        southWindows: [3, 2],
        eastWindows: [2, 2],
        northWindows: [3, 2]
    })

    door.copy({
        addTo: church.westWall,
        translate: { x: -3.5 }
    })
    door.copy({
        addTo: church.westWall,
        translate: { x: 3.5 }
    })

    // big circle window
    new Zdog.Ellipse({
        diameter: 8,
        addTo: church.westWall,
        translate: { y: -22 },
        color: navy
    });

    // ----- bell tower ----- //

    (function() {
        const bellTowerAnchor = new Zdog.Anchor({
            addTo: churchAnchor,
            translate: { x: -7, y: -36, z: -4 }
        })

        // tower ledge
        new Zdog.Rect({
            width: 8,
            height: 8,
            addTo: bellTowerAnchor,
            translate: { y: -12 },
            rotate: { x: TAU / 4 },
            color: navy
        })

        const wallColors = [red, white, gold, blue]
        const accentColors = [navy, blue, red, navy]
        const roofColors = [navy, gold, red, navy]

        for (let i = 0; i < 4; i++) {
            const wallAnchor = new Zdog.Anchor({
                addTo: bellTowerAnchor,
                rotate: { y: TAU / 4 * -i }
            })
            const bottomWallGroup = new Zdog.Group({
                addTo: wallAnchor,
                translate: { z: 4 }
            })

            const wallColor = wallColors[i]
            const accentColor = accentColors[i]
            const roofColor = roofColors[i]

            // bottom wall
            new Zdog.Rect({
                width: 8,
                height: 12,
                addTo: bottomWallGroup,
                translate: { y: -6 },
                color: wallColor
            })
            // circle cut-out
            new Zdog.Ellipse({
                diameter: 4,
                addTo: bottomWallGroup,
                translate: { y: -4 },
                color: accentColor
            })
            // top stripe
            new Zdog.Rect({
                width: 8,
                height: 2,
                addTo: bottomWallGroup,
                translate: { y: -9 },
                color: accentColor
            })

            const topWallGroup = new Zdog.Group({
                addTo: wallAnchor,
                translate: { y: -12, z: 3 }
            })
            // top wall
            new Zdog.Rect({
                width: 6,
                height: 7,
                addTo: topWallGroup,
                translate: { y: -3.5 },
                color: wallColor
            })
            // top window
            new Zdog.Rect({
                width: 2,
                height: 5,
                addTo: topWallGroup,
                translate: { y: -2.5 },
                color: accentColor
            })

            // roof
            new Zdog.Shape({
                path: [
                    { x: 0, y: 0, z: 0 },
                    { x: -3, y: 6, z: 3 },
                    { x: 3, y: 6, z: 3 }
                ],
                addTo: wallAnchor,
                translate: { y: -25 },
                color: roofColor
            })
        }

        // roof connectors
        // south, white side
        new Zdog.Shape({
            path: [
                { z: 4, y: 0 },
                { z: -4, y: -1 },
                { z: -4, y: 8 }
            ],
            addTo: bellTowerAnchor,
            translate: { x: 4 },
            color: white
        })
        // east gold side
        const connector = new Zdog.Rect({
            width: 8,
            height: 10,
            addTo: bellTowerAnchor,
            translate: { z: -4, y: 4 },
            color: gold
        })
        // north blue side
        connector.copy({
            translate: { x: -4, y: 4 },
            rotate: { y: TAU / 4 },
            color: blue
        })
    })()

    // ----- hill ----- //

    new Zdog.Shape({
        path: [
            { x: 0, y: 2 },
            { x: 10, y: 2 },
            {
                bezier: [
                    { x: 14, y: 2 },
                    { x: 20, y: 10 },
                    { x: 24, y: 10 }
                ]
            },
            { x: 30, y: 10 },
            {
                arc: [
                    { x: 34, y: 10 },
                    { x: 34, y: 14 }
                ]
            },
            // bring it back into hill
            { x: 14, y: 14, z: 0 }
        ],
        addTo: town,
        translate: { x: -6, y: -20, z: -12 },
        stroke: 4,
        color: gold
    })

    // ----- lil pyramids ----- //

    // front in front of left building
    lilPyramid({
        addTo: town,
        translate: { x: 6, z: 35, y: -4 }
    })

    // behind left building

    lilPyramid({
        addTo: town,
        translate: { x: -34, z: 20, y: -4 }
    })

    // front right
    lilPyramid({
        addTo: town,
        translate: { x: 35, z: 8, y: -4 }
    })

    lilPyramid({
        addTo: town,
        translate: { x: 31, z: -2, y: -4 }
    })
    // in front of church
    lilPyramid({
        addTo: town,
        translate: { x: 22, z: -28, y: -4 }
    })

    // ----- hedges ----- //

    // to right of front building
    hedge({
        addTo: town,
        translate: { x: 24, y: -4, z: 4 }
    })

    // right of church
    hedge({
        addTo: town,
        translate: { x: -4, y: -4, z: -42 }
    })
    // in between tower & church
    hedge({
        addTo: town,
        translate: { x: -30, y: -4, z: -18 }
        // color: gold,
    })

    hedge({
        addTo: town,
        translate: { x: 9, y: -4, z: -17 }
        // color: gold,
    })

    // ----- sun ----- //

    new Zdog.Shape({
        addTo: town,
        translate: { x: -6, y: -52, z: -42 },
        stroke: 6,
        color: gold
    })

    // ----- sky particles ----- //

    // dot above left building
    const skyDot = new Zdog.Shape({
        translate: { x: -3, y: -48, z: 42 },
        addTo: town,
        stroke: 2,
        color: white
    })

    // in front of church
    skyDot.copy({
        translate: { x: 30, y: -28, z: -28 }
    })

    const skyDiamond = new Zdog.Shape({
        path: [
            { x: 0, y: -1 },
            { x: 1, y: 0 },
            { x: 0, y: 1 },
            { x: -1, y: 0 }
        ],
        addTo: town,
        translate: { x: -27, y: -45, z: 29 },
        scale: 0.75,
        stroke: 0.5,
        color: white
    })
    skyDiamond.copy({
        rotate: { y: TAU / 4 }
    })

    const skyDiamond2 = skyDiamond.copy({
        translate: { x: 8, y: -34, z: -42 }
    })
    skyDiamond2.copy({
        rotate: { y: TAU / 4 }
    })

    const skyStar = new Zdog.Shape({
        path: [
            { x: 0, y: -1 },
            {
                arc: [
                    { x: 0, y: 0 },
                    { x: 1, y: 0 }
                ]
            },
            {
                arc: [
                    { x: 0, y: 0 },
                    { x: 0, y: 1 }
                ]
            },
            {
                arc: [
                    { x: 0, y: 0 },
                    { x: -1, y: 0 }
                ]
            },
            {
                arc: [
                    { x: 0, y: 0 },
                    { x: 0, y: -1 }
                ]
            }
        ],
        addTo: town,
        translate: { x: -39, y: -51, z: 12 },
        scale: 1.5,
        stroke: 1,
        color: white
    })
    skyStar.copy({
        rotate: { y: TAU / 4 }
    })

    // up front
    const skyStar2 = skyStar.copy({
        translate: { x: 29, y: -42, z: 30 },
        color: white
    })
    skyStar2.copy({
        rotate: { y: TAU / 4 }
    })

    // ----- clouds ----- //

    const cloud = new Zdog.Ellipse({
        addTo: town,
        diameter: 3,
        quarters: 2,
        translate: { x: -30, y: -56, z: 10 },
        rotate: { y: TAU / 4, z: -TAU / 4 },
        stroke: 2,
        closed: true,
        color: white
    })
    cloud.copy({
        translate: { x: -30, y: -57, z: 6 }
    })
    cloud.copy({
        translate: { x: -30, y: -56, z: 2 }
    })

    // line underneath
    new Zdog.Shape({
        addTo: town,
        path: [{ x: -1 }, { x: 1 }],
        translate: { x: -30, y: -56, z: 6 },
        scale: { x: 2 },
        rotate: { y: TAU / 4 },
        stroke: 2,
        color: white
    })

    // ----- flat earth ----- //

    const flatEarth = new Zdog.Ellipse({
        diameter: 128,
        addTo: illo,
        translate: town.translate,
        rotate: { x: TAU / 4 },
        stroke: 8,
        color: navy
    })

    // ----- sky ----- //

    const sky = new Zdog.Group({
        addTo: illo,
        translate: town.translate
        // translate: { y: 2 },
    });

    (function() {
        const topYs = [
            -64, -64, -52, -52,
            -44, -44, -36, -36,
            -44, -44, -52, -52,
            -60, -60, -52, -52
        ]
        const bottomYs = [
            -24, -24, -16, -16,
            -8, -8, -0, -0,
            -8, -8, -16, -16,
            -24, -24, -32, -32
        ]
        const radius = 64
        const skyPanelCount = topYs.length
        const angle = TAU / skyPanelCount
        const panelWidth = Math.tan(angle / 2) * radius * 2
        for (let i = 0; i < skyPanelCount; i++) {
            const nextI = (i + 1) % skyPanelCount
            const topYA = topYs[i]
            const topYB = topYs[nextI]
            const bottomYA = bottomYs[i]
            const bottomYB = bottomYs[nextI]
            const panelAnchor = new Zdog.Anchor({
                addTo: sky,
                rotate: { y: angle * i - TAU / 4 },
                translate: { y: 1 }
            })
            new Zdog.Shape({
                path: [
                    { x: -panelWidth / 2, y: topYA },
                    {
                        bezier: [
                            { x: 0, y: topYA },
                            { x: 0, y: topYB },
                            { x: panelWidth / 2, y: topYB }
                        ]
                    },
                    { x: panelWidth / 2, y: bottomYB },
                    {
                        bezier: [
                            { x: 0, y: bottomYB },
                            { x: 0, y: bottomYA },
                            { x: -panelWidth / 2, y: bottomYA }
                        ]
                    }
                ],
                addTo: panelAnchor,
                translate: { z: -radius },
                color: blue,
                stroke: 1,
                backface: false
            })
        }
    })()

    // -- animate --- //

    let t = 0
    const tSpeed = 1 / 120
    let then = new Date() - 1 / 60

    function animate() {
        update()
        render()
        requestAnimationFrame(animate)
    }

    animate()

    // -- update -- //

    function update() {
        const now = new Date()
        const delta = now - then

        if (isSpinning) {
            t += tSpeed * delta / 60
            const theta = Zdog.easeInOut(t % 1) * TAU
            const rev = 1
            const spin = -theta * rev + TAU / 8
            const extraRotation = TAU * rev * Math.floor((t % 4))
            illo.rotate.y = spin - extraRotation
            const everyOtherCycle = t % 2 < 1
            illo.rotate.x = everyOtherCycle ? 0 : (Math.cos(theta) * -0.5 + 0.5) * TAU * -1 / 8
        }
        illo.normalizeRotate()

        // rotate
        illo.updateGraph()

        then = now
    }

    // -- render -- //

    function render() {
        const ctx = illo.ctx
        illo.prerenderCanvas()

        // render shapes
        const isCameraXUp = illo.rotate.x < 0 || illo.rotate.x > TAU / 2

        sky.renderGraphCanvas(ctx)

        // HACK sort flat earth & town shapes manually
        if (isCameraXUp) {
            flatEarth.renderGraphCanvas(ctx)
        }
        town.renderGraphCanvas(ctx)
        if (!isCameraXUp) {
            flatEarth.renderGraphCanvas(ctx)
        }

        illo.postrenderCanvas()
        ctx.restore()
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王八八。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值