Login
芋圆社区 > 编程 > 【项目】桌宠之旅 > 12-实现桌宠播放语音

12-实现桌宠播放语音

846
0
2023-07-08
2023-07-18
Hey、小怪兽

  • • 要想播放语音,首先要准备语音,来到B站的Wiki下,来到可可萝的页面:
  • • 这里都是可可萝的语音了,F12打开控制台,点击这个选择元素,或者按Ctrl+Shift+C:
  • • 点击这个图标:
  • • 这边就显示了这个音频的地址了,双击这个地址,复制到新的窗口打开:
  • • 点击这个有个下载:
  • • 拿到所有的音频后,将音频文件重命名好放入/src/modules/kkr/voice文件夹下:
  • • 在渲染进程/src/_renderer/renderer.js写变量:
  • // 语音状态
    let talk_static = false;
    // 音频的dom
    let audio_dom = null;
  • • 在初始化的时候拿到音频dom;
  • audio_dom = document.getElementById("taro-pet-audio");
  • • 接着之前的定时动作需要修改,在播放语音的时候也不要做随机动作:
  • if (drag_static === false && talk_static === false) {
         // 其他代码
    } else {
         // 说明是在拖拽,什么都不做
    }
  • • 修改左键鼠标事件:
  • - 当没有拖拽和播放语音的时候可以播放语音
  • - 先设置talk_static播放语音状态为true
  • - 再设置播放路径,src就是/src/modules/kkr/voice/1.mp3,这里先写死,后面和词板一起随机
  • - 接着是监听播放完毕,播放完毕后talk_static设置为false
  • - 最后添加监听事件,音频播放的方法play()
  • // 左键点击播放语音
    anim.on("click", (event) => {
        if (drag_static === false && talk_static === false) {
            talk_static = true;
            // 设置音频文件的 URL
            audio_dom.src = `${file_prefix}/${current_module}/voice/1.mp3`;
            const onAudioEnded = () => {
                talk_static = false;
                // 移除事件监听器
                audio_dom.removeEventListener("ended", onAudioEnded);
            };
            audio_dom.addEventListener("ended", onAudioEnded);
            audio_dom.play();
        }
    });
  • • 下面就要去写音频的dom元素了,来到/src/index.html,添加:
  • <audio id="taro-pet-audio" src="" controls="" preload=""></audio>
  • • 来到/src/public/css/index.css写样式:
  • #taro-pet-audio {
        display: none;
    }
  • • 接着还是在终端pnpm start,左键鼠标点击桌宠本体,成功播放语音!
  • • 渲染进程/src/_renderer/renderer.js完整代码:
  • // 导入方法
    import { get_pixel_color_func, body_area_func } from "../utils/anim.js";
    // 当前使用的模型
    let current_module = "kkr";
    // 初始配置和动作配置
    let init_config = null;
    let action_config = null;
    // 当前的动画
    let anim_current = null;
    // 初始动画
    let anim_normal = null;
    // 拖拽动画
    let anim_drap = null;
    // 存储的随机动作索引,用来给定时器随机
    let anim_action_random_index = [];
    let anim_action_random_cache = [];
    // 动画的定时器
    let timer = null;
    // 拖拽状态
    let drag_static = false;
    // 语音状态
    let talk_static = false;
    // 动画的舞台
    let app = null;
    // 音频的dom
    let audio_dom = null;
    // 动画的canvas
    let anim_canvas = null;
    // 虚拟的canvas
    let shadow_canvas = null;
    // 获取窗口的大小
    const bower_width = window.innerWidth;
    const bower_height = window.innerHeight;
    
    // 页面加载完成执行
    window.addEventListener("load", async (event) => {
        // 获取配置
        const module = await import(`../modules/${current_module}/${current_module}.js`);
        init_config = module.init_config;
        action_config = module.action_config;
    
        // 赋值canvas
        anim_canvas = document.getElementById("taro-canvas");
        shadow_canvas = document.getElementById("shadow-canvas");
        shadow_canvas.width = bower_width;
        shadow_canvas.height = bower_height;
    
        audio_dom = document.getElementById("taro-pet-audio");
    
        // 动画舞台配置
        app = new PIXI.Application({
            view: anim_canvas,
            width: bower_width,
            height: bower_height,
            backgroundAlpha: 0,
            resolution: 1,
        });
        // 添加给div-taropet元素
        document.getElementById("taro-pet").appendChild(app.view);
    
    
        // 先把初始的动画加载完成
        anim_normal = await create_anim_func(init_config[0], 0);
        anim_normal.play();
        app.stage.addChild(anim_normal);
        // 赋值给当前动画
        anim_current = anim_normal;
    
        // 开始初始化其他的动画
        anim_drap = await create_anim_func(init_config.find((obj) => obj.name === "drap"), 0);
    
        // 将动作的配置转换成随机索引赋值 [0,1,2,3,4]
        anim_action_random_index = Array.from(
            action_config.map((obj, index) => {
                obj.index = index;
                return obj;
            }),
            ({ index }) => index
        );
    
        // 开启定时器
        setIntervalTimer();
    });
    
    // 创建动画的方法 obj-配置对象, type-是否初始化0/1
    const create_anim_func = async (obj, type) => {
        // 存放文件前缀, 文件格式(png,jpg)
        const file_prefix = "./modules";
        const file_format = ".png";
        const { name, frames, object } = obj;
        const texture_array = [];
        // 通过帧数循环获取贴图
        for (let i = 0; i < frames; i++) {
            const num = `000${i}`.slice(-3);
            // texture_name ./modules/kkr/normal/001.png
            const texture_name = type === 0 ? `${file_prefix}/${current_module}/${name}/${num}${file_format}` : `${file_prefix}/${current_module}/action/${name}/${num}${file_format}`;
            const texture = await PIXI.Texture.from(texture_name);
            texture_array.push(texture);
        }
        // 生成动画,配置动画属性
        const anim = new PIXI.AnimatedSprite(texture_array);
        anim.name = name;
        anim.animationSpeed = 0.5;
        anim.loop = object.loop;
    
        // 设置交互模式
        anim.eventMode = "dynamic";
    
        // 鼠标移动事件
        anim.on("mousemove", (event) => {
            const global_position = event.data.global;
            const local_position = anim.toLocal(global_position);
            // 当前这一帧的动画贴图
            const anim_img = anim.texture.baseTexture.resource.source;
    
            if (drag_static) {
                // 这个时候在拖拽,什么都不做
            } else {
                body_area_func(get_pixel_color_func(local_position.x, local_position.y, anim_img), local_position)
            }
        });
    
        // 左键点击播放语音
        anim.on("click", (event) => {
            if (drag_static === false && talk_static === false) {
                talk_static = true;
                // 设置音频文件的 URL
                audio_dom.src = `${file_prefix}/${current_module}/voice/1.mp3`;
                const onAudioEnded = () => {
                    talk_static = false;
                    // 移除事件监听器
                    audio_dom.removeEventListener("ended", onAudioEnded);
                };
                audio_dom.addEventListener("ended", onAudioEnded);
                audio_dom.play();
            }
        });
    
        // 鼠标点击右键拖拽
        anim.on("rightclick", (event) => {
            const global_position = event.data.global;
            const local_position = anim.toLocal(global_position);
            if (drag_static === false) {
                // 如果没在拖拽状态,右键后进入推拽状态,传给主进程点击的位置
                window.mouseAPI.mouseDrapStart({
                    x: local_position.x,
                    y: local_position.y,
                    drap: true,
                });
                // 开启拖拽状态进入拖拽动画
                drag_static = true;
                change_anim_func(anim_current, anim_drap, 0);
            } else {
                // 再次点击脱离拖拽状态
                window.mouseAPI.mouseDrapEnd({
                    drap: false,
                });
                // 取消拖拽状态进入普通动画
                drag_static = false;
                change_anim_func(anim_drap, anim_normal, 0);
            }
        });
    
        if (object.loop === false) {
            anim.onComplete = () => {
                // 完成动作后500毫秒后进入普通动画
                change_anim_func(anim, anim_normal, object?.endTime ?? 100);
            };
        }
    
        if (type === 1) {
            // 缓存随机动作,这样下次不需要再次生成
            anim_action_random_cache.push(anim);
            // 给生成动画时间
            setTimeout(() => {
                // 生成动画后1秒后进入动作动画
                if (drag_static === false) {
                    change_anim_func(anim_normal, anim, 0);
                }
            }, 1000);
        } else {
            // 如果是初始动画的话就返回动画
            return anim;
        }
    };
    
    // 进入动画,可以用来切换动画(切换回normal或进入drap)
    const change_anim_func = (from_anim, to_anim, time) => {
        from_anim.stop();
        setTimeout(() => {
            app.stage.removeChild(from_anim);
            to_anim.gotoAndPlay(0);
            app.stage.addChild(to_anim);
            // 替换当前动画
            anim_current = to_anim;
        }, time);
    };
    
    // 设置定时器,用来一定时间播放一次随机动作
    const setIntervalTimer = () => {
        timer = setInterval(() => {
            if (drag_static === false && talk_static === false) {
                // 随机获取动作的index
                const index = Math.floor(Math.random() * anim_action_random_index.length);
                // 通过index获取动作的配置
                const action = action_config[anim_action_random_index[index]];
                // 如果有缓存的动作就不需要生成了
                const cacheAction = anim_action_random_cache.find((obj) => obj.name === action.name);
    
                if (cacheAction) {
                    change_anim_func(anim_normal, cacheAction, 0);
                } else {
                    create_anim_func(action, 1);
                }
            } else {
                // 说明是在拖拽,什么都不做
            }
        }, 1000 * 60 * 5);
    };
  • • /src/index.html完整代码:
  • <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>TaroPet</title>
        <link rel="stylesheet" href="./public/css/index.css" />
    </head>
    <body>
        <div id="taro-pet" class="taro-pet">
            <canvas id="taro-canvas"></canvas>
            <canvas id="shadow-canvas"></canvas>
        </div>
    
        <audio id="taro-pet-audio" src="" controls="" preload=""></audio>
    
        <script src="./public/js/pixi.min.js"></script>
        <script src="./_renderer/renderer.js" type="module"></script>
    </body>
    </html>
  • • /src/public/css/index.css完整代码:
  • * {
        padding: 0;
        margin: 0;
        box-sizing: border-box;
    }
    
    html,
    body {
        width: 100vw;
        height: 100vh;
        overflow: hidden;
    }
    
    .taro-pet {
        width: 100vw;
        height: 100vh;
        transform: scaleX(-1);
    }
    
    #taro-pet-audio {
        display: none;
    }
    
    #shadow-canvas {
        position: absolute;
        z-index: -1;
        opacity: 0;
    }
    

上一篇:11-实现桌宠随机动作

下一篇:13-实现桌宠词板功能

Message Board
回复
回复内容不允许为空
留言字数要大于2,小于200!
提交成功,5s后刷新页面!
编程导航

0-桌宠开发前言

1-搭建环境和创建简单的应用程序

2-修改项目结构

3-创建一个透明的窗口

4-nodemon实现热加载

5-获取桌宠每一帧图片

6-Pixi.js加载桌宠初始动画

7-添加鼠标事件

8-获取区域颜色和判断桌宠本体

9-设置进程通信和鼠标穿透

10-实现桌宠拖拽

11-实现桌宠随机动作

12-实现桌宠播放语音

13-实现桌宠词板功能

14-设置系统托盘

15-实现桌宠模组切换

16-桌宠数据和状态拓展

17-生成ICO图标和准备打包

18-完成开发并打包桌宠应用

Copyright © 2020 芋圆社区

Powered by 浙ICP备2020039309号-1

此页面不支持夜间模式!

已进入夜间模式!

已进入普通模式!

搜索框不允许为空

签到成功!经验+5!芋圆币+2!

签到失败!今日已签到!

需要登录社区账号才可以进入!

复制成功
寄,页面未加载完成或页面无锚点