H5 火柴人科目三和GitHub获取仓库点星星的用户列表发生了艺术的碰撞

news/2025/2/24 13:31:28

先看效果,代码写的比较乱,有待优化

效果

https://linyisonger.github.io/H5.Examples/?name=./089.%E7%9C%8B%E6%98%9F%E6%98%9F%E7%9A%84%E8%88%9E%E8%80%85.html
在这里插入图片描述

思路

看起来很简单,实则也不是很难,就是需要思路要打开。
一开始的流程思路是

思路一

  1. 通过视频获取骨骼节点动画 ✔
  2. 使用AI文生图+骨骼节点生成人物信息 ❌ 效果不达预期
  3. 确定人物头部位置+序列帧动画

思路二

  1. 通过视频获取骨骼节点动画 ✔
  2. 通过骨骼动画进行canvas渲染,节点连接从而打到火柴人的效果。✔
  3. 确定人物头部位置+序列帧动画 ❌ 画布太大无法渲染一张图
  4. 确定人物头部位置+序列帧动画 + JSON存储 ✔

实现

  1. 通过视频播放+requestAnimationFrame获取每帧图片
  2. 通过@tensorflow/tfjs+@tensorflow-models/posenet来获取图片骨骼节点
  3. 通过canvas进行骨骼连接

这又是一篇新的内容,AI方面不是很了解,只是看着教程做的。
https://linyisonger.github.io/H5.Examples/?name=./090.%E7%81%AB%E6%9F%B4%E4%BA%BA%E7%94%9F%E6%88%90%E5%99%A8.html
在这里插入图片描述
上传视频后输出的JSON文件是这个示例所需要的。

里面包含每一帧的火柴人Base64图片,头像应该放置的位置。

代码

获取GitHub仓库点星星的用户列表

⚠ 当然这不是很好的写法,一旦出现报错就是死循环

/**
 * 获取star的用户 默认30一页
 * @author 	 linyisonger
 * @date 	 2025-02-18
 */
async function getStargazers(page = 1) {
    const result = await fetch(`https://api.github.com/repos/linyisonger/H5.Examples/stargazers?page=${page}`)
    return await result.json()
}

/**
 * 获取所有star的用户
 * @author 	 linyisonger
 * @date 	 2025-02-18
 */
async function getAllStargazers(page = 1, users = []) {
    let stargazers = await getStargazers(page)
    users = users.concat(stargazers)
    if (stargazers.length < 30) return users
    return await getAllStargazers(page + 1, users)
}

其他的感觉没什么重点

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="./assets/global.css">

    <style>
        #container {
            display: flex;
            justify-content: center;
            align-items: center;
            flex-wrap: wrap;
            height: 100vh;
            align-content: center;
            position: relative;
        }

        #container canvas {
            margin-top: -140px
        }

        .welcome-statement {
            position: absolute;
            top: 100px;
            font-size: 40px;
            color: #999;
        }

        .join-us {
            position: absolute;
            bottom: 200px;
            z-index: 100;
            display: inline-flex;
            padding: 0 20px 3px;
            line-height: 40px;
            background: linear-gradient(to bottom, rgb(87, 196, 245), rgb(26, 147, 206));
            color: rgb(254, 252, 255);
            cursor: pointer;
            border-radius: 4px;
            font-weight: bold;
            box-shadow: inset 0px -3px 0 rgb(19, 98, 139);
        }

        .join-us:active {
            opacity: .7;
            box-shadow: inset 0px 0px 0 transparent;
        }

        .bgm-controller {
            position: absolute;
            right: 20px;
            top: 20px;
            width: 40px;
        }

        .bgm-controller:active {
            opacity: .7;
        }
    </style>
</head>

<body>
    <div id="container">
        <!-- <audio class="bgm" muted="true">
            <source src="./assets/dance/swing-dance.mp3" />
        </audio> -->
        <video class="bgm" muted style="display: none;">
            <source src="./assets/dance/kemusan.mp4" />
        </video>


        <div class="welcome-statement">感谢各位给 H5.Examples 点⭐⭐~</div>

        <a class="join-us" href="https://github.com/linyisonger/H5.Examples">
            加入我们
        </a>


        <img class="bgm-controller" src="./assets/dance/bgm-c.png">

    </div>
    <script type="module">
        /**
         * 加载图
         * @param {string} src
         * @returns {Promise<HTMLImageElement>}
         */
        function loadImage(src) {
            return new Promise((resolve) => {
                let image = new Image()
                image.src = src;
                image.onload = (ev) => {
                    resolve(image)
                }
            })
        }

        /**
         * 加载音乐
         * @param {string} src
         * @returns {Promise<HTMLImageElement>}
         */
        function loadAudio(src) {
            return new Promise((resolve) => {
                let audio = new Audio(src)
                audio.addEventListener("loadeddata", resolve)
            })
        }

        /**
         * 获取star的用户 默认30一页
         * @author 	 linyisonger
         * @date 	 2025-02-18
         */
        async function getStargazers(page = 1) {
            const result = await fetch(`https://api.github.com/repos/linyisonger/H5.Examples/stargazers?page=${page}`)
            return await result.json()
        }

        /**
         * 获取所有star的用户
         * @author 	 linyisonger
         * @date 	 2025-02-18
         */
        async function getAllStargazers(page = 1, users = []) {
            let stargazers = await getStargazers(page)
            users = users.concat(stargazers)
            if (stargazers.length < 30) return users
            return await getAllStargazers(page + 1, users)
        }



        // getAllStargazers().then((res) => {
        //     console.log("获取star的用户", res);
        // })

        let dancers = [
            {
                "login": "AnChangSu",
                "id": 5037050,
                "node_id": "MDQ6VXNlcjUwMzcwNTA=",
                "avatar_url": "https://avatars.githubusercontent.com/u/5037050?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/AnChangSu",
                "html_url": "https://github.com/AnChangSu",
                "followers_url": "https://api.github.com/users/AnChangSu/followers",
                "following_url": "https://api.github.com/users/AnChangSu/following{/other_user}",
                "gists_url": "https://api.github.com/users/AnChangSu/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/AnChangSu/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/AnChangSu/subscriptions",
                "organizations_url": "https://api.github.com/users/AnChangSu/orgs",
                "repos_url": "https://api.github.com/users/AnChangSu/repos",
                "events_url": "https://api.github.com/users/AnChangSu/events{/privacy}",
                "received_events_url": "https://api.github.com/users/AnChangSu/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "HGinGitHub",
                "id": 103415496,
                "node_id": "U_kgDOBin-yA",
                "avatar_url": "https://avatars.githubusercontent.com/u/103415496?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/HGinGitHub",
                "html_url": "https://github.com/HGinGitHub",
                "followers_url": "https://api.github.com/users/HGinGitHub/followers",
                "following_url": "https://api.github.com/users/HGinGitHub/following{/other_user}",
                "gists_url": "https://api.github.com/users/HGinGitHub/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/HGinGitHub/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/HGinGitHub/subscriptions",
                "organizations_url": "https://api.github.com/users/HGinGitHub/orgs",
                "repos_url": "https://api.github.com/users/HGinGitHub/repos",
                "events_url": "https://api.github.com/users/HGinGitHub/events{/privacy}",
                "received_events_url": "https://api.github.com/users/HGinGitHub/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "harris2012",
                "id": 12846977,
                "node_id": "MDQ6VXNlcjEyODQ2OTc3",
                "avatar_url": "https://avatars.githubusercontent.com/u/12846977?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/harris2012",
                "html_url": "https://github.com/harris2012",
                "followers_url": "https://api.github.com/users/harris2012/followers",
                "following_url": "https://api.github.com/users/harris2012/following{/other_user}",
                "gists_url": "https://api.github.com/users/harris2012/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/harris2012/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/harris2012/subscriptions",
                "organizations_url": "https://api.github.com/users/harris2012/orgs",
                "repos_url": "https://api.github.com/users/harris2012/repos",
                "events_url": "https://api.github.com/users/harris2012/events{/privacy}",
                "received_events_url": "https://api.github.com/users/harris2012/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "Lavenir7",
                "id": 105573717,
                "node_id": "U_kgDOBkrtVQ",
                "avatar_url": "https://avatars.githubusercontent.com/u/105573717?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/Lavenir7",
                "html_url": "https://github.com/Lavenir7",
                "followers_url": "https://api.github.com/users/Lavenir7/followers",
                "following_url": "https://api.github.com/users/Lavenir7/following{/other_user}",
                "gists_url": "https://api.github.com/users/Lavenir7/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/Lavenir7/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/Lavenir7/subscriptions",
                "organizations_url": "https://api.github.com/users/Lavenir7/orgs",
                "repos_url": "https://api.github.com/users/Lavenir7/repos",
                "events_url": "https://api.github.com/users/Lavenir7/events{/privacy}",
                "received_events_url": "https://api.github.com/users/Lavenir7/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "linyisonger",
                "id": 34770610,
                "node_id": "MDQ6VXNlcjM0NzcwNjEw",
                "avatar_url": "https://avatars.githubusercontent.com/u/34770610?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/linyisonger",
                "html_url": "https://github.com/linyisonger",
                "followers_url": "https://api.github.com/users/linyisonger/followers",
                "following_url": "https://api.github.com/users/linyisonger/following{/other_user}",
                "gists_url": "https://api.github.com/users/linyisonger/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/linyisonger/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/linyisonger/subscriptions",
                "organizations_url": "https://api.github.com/users/linyisonger/orgs",
                "repos_url": "https://api.github.com/users/linyisonger/repos",
                "events_url": "https://api.github.com/users/linyisonger/events{/privacy}",
                "received_events_url": "https://api.github.com/users/linyisonger/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "lpleipeng",
                "id": 39250004,
                "node_id": "MDQ6VXNlcjM5MjUwMDA0",
                "avatar_url": "https://avatars.githubusercontent.com/u/39250004?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/lpleipeng",
                "html_url": "https://github.com/lpleipeng",
                "followers_url": "https://api.github.com/users/lpleipeng/followers",
                "following_url": "https://api.github.com/users/lpleipeng/following{/other_user}",
                "gists_url": "https://api.github.com/users/lpleipeng/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/lpleipeng/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/lpleipeng/subscriptions",
                "organizations_url": "https://api.github.com/users/lpleipeng/orgs",
                "repos_url": "https://api.github.com/users/lpleipeng/repos",
                "events_url": "https://api.github.com/users/lpleipeng/events{/privacy}",
                "received_events_url": "https://api.github.com/users/lpleipeng/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            },
            {
                "login": "xxxggg-ctrl",
                "id": 63829555,
                "node_id": "MDQ6VXNlcjYzODI5NTU1",
                "avatar_url": "https://avatars.githubusercontent.com/u/63829555?v=4",
                "gravatar_id": "",
                "url": "https://api.github.com/users/xxxggg-ctrl",
                "html_url": "https://github.com/xxxggg-ctrl",
                "followers_url": "https://api.github.com/users/xxxggg-ctrl/followers",
                "following_url": "https://api.github.com/users/xxxggg-ctrl/following{/other_user}",
                "gists_url": "https://api.github.com/users/xxxggg-ctrl/gists{/gist_id}",
                "starred_url": "https://api.github.com/users/xxxggg-ctrl/starred{/owner}{/repo}",
                "subscriptions_url": "https://api.github.com/users/xxxggg-ctrl/subscriptions",
                "organizations_url": "https://api.github.com/users/xxxggg-ctrl/orgs",
                "repos_url": "https://api.github.com/users/xxxggg-ctrl/repos",
                "events_url": "https://api.github.com/users/xxxggg-ctrl/events{/privacy}",
                "received_events_url": "https://api.github.com/users/xxxggg-ctrl/received_events",
                "type": "User",
                "user_view_type": "public",
                "site_admin": false
            }
        ]


        let bgmControllerDom = document.querySelector('.bgm-controller')
        bgmControllerDom.addEventListener("click", () => {
            const bgm = document.body.querySelector('.bgm')
            bgm.muted = !bgm.muted;
            bgmControllerDom.setAttribute('src', bgm.muted ? './assets/dance/bgm-c.png' : './assets/dance/bgm-o.png')
        })

        // 2D火柴人 贴图

        function fetchLoad(url) {
            return new Promise((resolve) => {
                fetch(url).then((response) => response.json()).then(resolve)
            })
        }

        async function initGame() {
            dancers = await getAllStargazers()
            let dance = await fetchLoad("./assets/dance/kemusan.json")
            const DROP_FRAME = 5 // 抽帧
            const ZOOM_OUT = .5
            // 检查动作信息
            // for (let i = 0; i < dance.frames.length; i++) {
            //     console.log(i);
            //     const frame = dance.frames[i];
            //     const img = document.createElement('img')
            //     img.src = frame.url;
            //     document.body.appendChild(img)
            // }

            let danceCvsList = []
            for (let i = 0; i < dancers.length; i++) {
                const dancer = dancers[i];
                let danceCvs = await createCanvas(dancer)
                danceCvsList.push({
                    dancer,
                    cvs: danceCvs
                })
            }


            let i = 0
            async function animationFrame() {
                if (i % DROP_FRAME == 0) {
                    for (let j = 0; j < danceCvsList.length; j++) {
                        const { cvs, dancer } = danceCvsList[j];
                        await drawFrame(cvs, dance.frames[(i / DROP_FRAME) % dance.frames.length], dancer.avatar_url)
                    }
                }
                requestAnimationFrame(animationFrame)
                i++;
            }
            await animationFrame()

            document.body.querySelector('.bgm').play()
            document.body.querySelector('.bgm').loop = true;
            /**
             * 创建一个用户
             * @author 	 linyisonger
             * @date 	 2025-02-23
             */
            function createCanvas(dancer) {
                let avatarUrl = dancer.avatar_url
                let cvs = document.createElement("canvas")
                cvs.setAttribute('width', dance.width)
                cvs.setAttribute('height', dance.height)
                cvs.width = dance.width * ZOOM_OUT;
                cvs.height = dance.height * ZOOM_OUT;
                document.body.querySelector("#container").appendChild(cvs)
                return cvs
            }
            /**
             * 渲染一帧
             * @author 	 linyisonger
             * @date 	 2025-02-23
             */
            async function drawFrame(cvs, frame, avatar) {
                /** @type {CanvasRenderingContext2D } */
                let ctx = cvs.getContext('2d')
                let roleImg = await loadImage(frame.url)
                let avatarImg = await loadImage(avatar)
                ctx.clearRect(0, 0, cvs.width, cvs.height)
                ctx.drawImage(roleImg, 0, 0, cvs.width, cvs.height)
                let avatarWidth = 40 * ZOOM_OUT
                ctx.drawImage(avatarImg, (frame.avatar.x * ZOOM_OUT - avatarWidth / 2), (frame.avatar.y * ZOOM_OUT - avatarWidth / 2), avatarWidth, avatarWidth)
            }
        }

        initGame()






    </script>

</body>

</html>

源码仓库

更新的话文章可能不一定会更新,仓库会可能更新,有问题可以提issue~

https://github.com/linyisonger/H5.Examples


http://www.niftyadmin.cn/n/5864385.html

相关文章

QT 基础知识点

1.基础窗口类QMainWindow qDialog Qwidget 随项目一起创建的窗口基类有三个可选QMainWindow qDialog Qwidget 1.1 Qwidget 是所有窗口的基类&#xff0c;只要是他的子类&#xff0c;或子类的子类&#xff0c;都具有他的属性。 右键项目 Add New -> Qt qt设计师界面类&am…

2025年SCI一区智能优化算法:混沌进化优化算法(Chaotic Evolution Optimization, CEO),提供MATLAB代码

一、混沌进化优化算法 https://github.com/ITyuanshou/MATLABCode 1. 算法简介 混沌进化优化算法&#xff08;Chaotic Evolution Optimization, CEO&#xff09;是2025年提出的一种受混沌动力学启发的新型元启发式算法。该算法的主要灵感来源于二维离散忆阻映射的混沌进化过…

DINOv2 + yolov8 + opencv 检测卡车的可拉拽雨覆是否完全覆盖

最近是接了一个需求咨询图像处理类的&#xff0c;甲方要在卡车过磅的地方装一个摄像头用检测卡车的车斗雨覆是否完全&#xff0c; 让我大致理了下需求并对技术核心做下预研究 开发一套图像处理软件&#xff0c;能够实时监控经过的卡车并判断其车斗的雨覆状态。 系统需具备以下…

蓝桥杯 Java B 组之背包问题(01背包、完全背包)

Day 1&#xff1a;背包问题&#xff08;01背包、完全背包&#xff09; &#x1f4d6; 一、背包问题简介 背包问题是动态规划&#xff08;DP&#xff09;中一个经典的优化问题&#xff0c;涉及物品选择和容量约束。通常分为以下几类&#xff1a; 01 背包&#xff08;0/1 Knaps…

49 set与map的模拟实现

目录 一、源码及框架分析 二、模拟实现map和set &#xff08;一&#xff09;复用红黑树的框架&#xff0c;并支持insert &#xff08;二&#xff09;支持迭代器的实现 &#xff08;三&#xff09;map支持 [ ] &#xff08;四&#xff09;整体代码实现 一、源码及框架分析…

【Go】Go wire 依赖注入

1. wire 简介 wire 是一个 Golang 的依赖注入框架&#xff08;类比 Spring 框架提供的依赖注入功能&#xff09; ⭐ 官方文档&#xff1a;https://github.com/google/wire 这里关乎到编程世界当中一条好用的设计原则&#xff1a;A用到了B&#xff0c;那么B一定是通过依赖注入的…

【CVPR2024-工业异常检测】PromptAD:与只有正常样本的少样本异常检测的学习提示

代码链接 摘要 摘要写作总结&#xff1a; 1.提出 两个关键点 &#xff08;视觉语言模型【模型】 少量工业异常检测【方向】&#xff09; 2.想要解决的问题 3.针对上述问题&#xff0c;本文提出了一种什么【方法】的什么【应用方面】方法【模型名】 4.具体讲方法的步骤 5.实验…

0基础玩转python(打怪升级篇)第一章1.1安装python编辑器

第一章 新手村 1.1合适的武器 &#xff08;安装python编辑器&#xff09; 新手村位于代码大陆的东北部&#xff0c;四面环山&#xff0c;高大的城墙外坐落着一条护城河。一位少年缓步走进城内&#xff0c;看到了城内集市热闹非凡。 你叫做“阿印” 是一位勇士 当前等级o Lv 最…