首页 > 其他分享 >【electron-vite+live2d+vue3+element-plus】实现桌面模型宠物+桌面管理系统应用(踩坑)

【electron-vite+live2d+vue3+element-plus】实现桌面模型宠物+桌面管理系统应用(踩坑)

时间:2024-07-02 17:24:27浏览次数:21  
标签:index vue const app element live2d 桌面 import any

脚手架

项目使用 electron-vite 脚手架搭建
ps:还有一个框架是 electron-vite ,这个框架我发现与pixi库有冲突,无法使用,如果不用pixi也可以用这个脚手架。
node 版本建议18+

----------------------------------------------------------------------------------------

运行live2D相关依赖

1.pixi.js

npm install [email protected]
// pixi 后面可能运行会报错,提示需要安装unsafe-eval 
// 需要注意pixi/unsafe-eval 需要安装与pixi一致的版本
npm install @pixi/[email protected]

2. pixi-live2d-display

npm install pixi-live2d-display

3. live2D官方SDK

如果需要兼容老版本模型需要引入2.0版sdk

因为使用Vite,SDK引入不能使用import,需要在index.html 中使用script标签引入

需要注意的是文件存放路径,否则打包后会找不到文件,这里笔者是在renderer文件夹下创建了public文件夹,将渲染进程需要使用的静态资源存放在里面。

----------------------------------------------------------------------------------------

VUE 路由设置与Element-Plus安装

npm install vue-router
npm install element-plus

安装后新建相关文件夹与文件
需要注意的是路由模式要使用hash模式

router/index.ts

import {
  createRouter,
  createWebHashHistory,
  type RouteLocationNormalized,
  createWebHistory
} from "vue-router";
import routes from "./routes";
const router = createRouter({
  // hash路由模式
  history: createWebHashHistory(),
  // History路由模式
  // history: createWebHistory(),
  routes
});

export interface toRouteType extends RouteLocationNormalized {
  meta: {
    title?: string;
    noCache?: boolean;
  };
}
router.beforeEach((to: toRouteType, from, next) => {
  next();
});
router.afterEach(() => {
});
export default router;

router/routes.ts

import Layout from "../layout/index.vue";
import type { RouteRecordRaw } from "vue-router";
const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    name: "root",
    component: Layout,
    redirect: "live2D",
    children: [
      {
        path: "live2D",
        name: "live2D",
        component: () => import("../views/live2D/index.vue"),
        meta: {
          title: "live2D"
        }
      }
    ]
  }
];

export default routes;

layout/index.vue

<script setup lang="ts">
import { computed } from "vue";
const cachedViews = computed(() => {
  return [];
});
</script>
<template>
  <div class="app-wrapper">
    <router-view v-slot="{ Component }">
      <keep-alive :include="cachedViews">
        <component :is="Component" />
      </keep-alive>
    </router-view>
  </div>
</template>

<style scoped>
.app-wrapper {
  position: relative;
  height: 100%;
  width: 100%;
}
</style>

App.vue 加入router-view标签

<template>
  <router-view />
</template>
<style></style>

renderer渲染进程 下的main.ts 引入相关包

import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router/index";
import ElementPlus from 'element-plus';
import './assets/element.css';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
const app = createApp(App);
app.use(router);
app.use(ElementPlus,{locale:zhCn});
app.mount('#app')

main主进程 下的 main.ts 跳转修改如下
默认为跳转根目录,跟目录的redirect配置的页面,需要指定页面使用hash拼接路由与参数。
参数在路由后加?xxx=xxx&yyy=yyy

 if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
   await mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
   // 跳转指定页面
   // await recordsListWindow.loadURL(process.env['ELECTRON_RENDERER_URL']+`/#/xxxx`);
 } else {
   await mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
   // 跳转指定页面
   // await recordsListWindow.loadFile(join(__dirname, '../renderer/index.html'),{hash:'/xxxx'});
 }

测试使用

在renderer/views文件夹下创建 views/live2D/index.vue,设置模型容器

<script setup lang="ts" name="live2D">
import { reactive,ref,onMounted } from 'vue'
import * as PIXI from 'pixi.js'
import * as pixiFnPatch from "@pixi/unsafe-eval"
import { Live2DModel } from 'pixi-live2d-display'
import jsonFile from '/model/xxx/xxx.model3.json?url'
// 全局注册
let windowRef:any = window;
windowRef.PIXI = PIXI;
// 修复@pixi/unsafe-eval无法正常安装问题
pixiFnPatch.install(PIXI);

async function initLive2D(){
  let model:any = await Live2DModel.from(jsonFile);
  const app = new PIXI.Application({
    view: document.getElementById('live2d-canvas') as HTMLCanvasElement,
    width: 100,
    height: 300,
    autoStart:true,
    backgroundAlpha:0
  });
  app.stage.addChild(model);
  // app.renderer.backgroundColor = 0x0161639;
  // transforms 模型方位
  model.x = -10; // 方位(单位像素)
  model.y = -20
  // model.rotation = Math.PI
  // model.skew.x = Math.PI
  model.scale.set(0.6)  // 缩放
  model.anchor.set(0, 0) // 锚点,以画布中心下方为中心点,x,y(单位:倍)
  model.on('hit', (hitAreas) => {
    // if (hitAreas.includes('body')) {
    //   model.motion('tap_body')
    // }
  })
}
onMounted(() => {
  initLive2D();
})
</script>

<template>
  <div class='canvas-wrap'>
    <canvas id="live2d-canvas" class="live2d-canvas" width="100" height="300"></canvas>
  </div>
</template>
<style scoped>
  .canvas-wrap{
    width: 100%;
    height: 100%;
    cursor: move;
    -webkit-app-region: drag;
  }
  .live2d-canvas{
    width: 100%;
    height: 100%;
  }
</style>

主进程中的main.ts文件创建主窗口

// 创建主窗体参数做如下更改
const { width,height} = screen.getPrimaryDisplay().workAreaSize;
const mainWindow = new BrowserWindow({
  x: width - 150,
  y: height - 300,
  width: 100,
  height: 300,
  show: false,
  maximizable: false,
  minimizable: false,
  resizable: false,
  fullscreenable: false,
  frame: false,
  transparent: true,
  hasShadow: false,
  alwaysOnTop: true,
  titleBarStyle: 'customButtonsOnHover',
  autoHideMenuBar: true,
  ...(process.platform === 'linux' ? { icon } : {}),
  webPreferences: {
    preload: join(__dirname, '../preload/index.js'),
    sandbox: false,
    nodeIntegration: true,
    webSecurity:false // 禁用同源策略
  }
})

----------------------------------------------------------------------------------------

测试效果-运行/打包

// 运行
npm run dev
// 打包 需要管理员权限 
// 打包后在根目录的 dist 下有安装包和安装后的文件夹
npm run build:win

具体模型交互使用相关API参数进行设置即可

----------------------------------------------------------------------------------------

配置vue页面demo

1.views文件夹下新建demo/index.vue

2.router.ts新增相关路由

{
	path: "demo",
  	name: "demo",
  	component: () => import("../views/demo/index.vue"),
  	meta: {
    	title: "demo"
  	}
},

3. 在主进程下的main.ts新增托盘菜单配置做测试

// 系统托盘图标目录
  const appTray = new Tray(icon);

  const menuTemplate = [
    {
      id: '1',
      label: '查看demo',
      click: async function () {
        let recordsListWindow = new BrowserWindow({
          width: 1000,
          height: 600,
          title: 'demo',
          autoHideMenuBar: true,
          webPreferences: {
            nodeIntegration: true
          }
        });
        if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
          await recordsListWindow.loadURL(process.env['ELECTRON_RENDERER_URL']+`/#/demo`);
        }
        else {
          await recordsListWindow.loadFile(join(__dirname, '../renderer/index.html'),{hash:'/demo'});
        }
        recordsListWindow.setTitle('demo');
        //打开开发者工具
        if(is.dev) recordsListWindow.webContents.openDevTools({mode:'detach'});
      }
    },
    {
      id: '2',
      label: '退出',
      click: function(){
        app.quit();
      }
    }
  ];
  // 图标的上下文菜单
  const contextMenu = Menu.buildFromTemplate(menuTemplate);
  // 设置此托盘图标的悬停提示内容
  appTray.setToolTip('demo');
  appTray.setTitle('demo');
  // 设置此图标的上下文菜单
  appTray.setContextMenu(contextMenu);

配置完成后托盘图标右键即可出现菜单,点击后会创建新窗口显示对应路由下的vue文件

----------------------------------------------------------------------------------------

开发中可能出现的报错

1. tsc 代码检测报错

在package.json 中把相关脚本的tsc检测关闭

----------------------------------------------------------------------------------------

2.打包后静态资源无法访问

检查是否使用绝对路径,最好的方法是将渲染进程的静态资源都放在public文件夹下
渲染进程资源处理
主进程资源处理

----------------------------------------------------------------------------------------

3.模型拖拽与鼠标事件冲突

有时候会遇到既要模型能拖动,也要能右键出现菜单的需求。
有两种解决方法:
(1):设置区域拖动,只有部分区域能触发拖动
能拖动的元素设置:-webkit-app-region: drag;
要触发鼠标事件的元素设置:-webkit-app-region: no-drag;
(2):不用-webkit-app-region: drag;属性来拖动
具体代码如下:

前端页面做事件监听
const moveIng = ref(false);
const startX = ref(0);
const startY = ref(0);
const lastWidth = ref(0);
const lastHeight = ref(0);
function move (event:any){
  if (!moveIng.value) return;
  const x:any = window.screenX + event.clientX - startX.value
  const y:any = window.screenY + event.clientY - startY.value
  // 调用主进程函数
  window.api.moveBounds(parseInt(x), parseInt(y), lastWidth.value, lastHeight.value);
}
window.addEventListener('mousedown',(event:any)=>{
 event.preventDefault();
  moveIng.value = true;
  startX.value = parseInt(event.clientX);
  startY.value = parseInt(event.clientY);
  lastWidth.value = window.outerWidth;
  lastHeight.value = window.outerHeight;
  document.addEventListener('mousemove', move);
});
window.addEventListener('mouseup',(event:any)=>{
  event.preventDefault();
  if (!moveIng.value) return
  document.removeEventListener('mousemove', move)
  moveIng.value = false
});
window.addEventListener('contextmenu',()=>{
  if (!moveIng.value) return
  document.removeEventListener('mousemove', move)
  moveIng.value = false
});

中间层 preload.ts

moveBounds: (x:any,y:any,width:any,height:any) => {
	ipcRenderer.send('moveBounds',x,y,width,height);
}

主进程 main.ts

// 监听渲染线程窗体移动同时改变主进程位置
  ipcMain.on('moveBounds', (event:any, x:any, y:any, width:any, height:any) => {
    if(event.frameId!=mainWindow.webContents.id) return;
    let newBounds = {
      x: parseInt(x),
      y: parseInt(y),
      width: parseInt(width),
      height: parseInt(height),
    }
    mainWindow.setBounds(newBounds)
  })

----------------------------------------------------------------------------------------

4.右键自定义菜单

// 给主窗口添加右键菜单
  const contextRightMenu = Menu.buildFromTemplate(menuTemplate);
  mainWindow.webContents.on("context-menu", (e:any) => {
    e.preventDefault();
    contextRightMenu.popup();
  });

----------------------------------------------------------------------------------------

5.在使用本地静态图片资源时报错

Refused to load the script xxxxxx because it violates the following Content Security Policy directive:"script-src 'self' xxxxxxxxxxxxx"
需要在index.html 中修改meta标签

    <meta http-equiv="Content-Security-Policy" content="default-src *; img-src * 'self' data: https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' *; style-src 'self' 'unsafe-inline' *">

----------------------------------------------------------------------------------------

6.修改打包图标

安装 electron-icon-builder 包
配置package.json,新增脚本,修改input路径为自己项目的路径

"build-icon": "electron-icon-builder --input=./resources/icon.png --output=build --flatten"

运行脚本即可生成

npm run build-icon

----------------------------------------------------------------------------------------

标签:index,vue,const,app,element,live2d,桌面,import,any
From: https://www.cnblogs.com/twelveblog/p/18280193

相关文章

  • termsrv.dll 是实现 Windows 远程桌面服务的核心组件,它通过上述机制和功能,支持了在企
    远程桌面协议(RemoteDesktopProtocol,RDP)是由微软开发的一种专用协议,用于在网络上进行远程桌面会话和远程应用程序的访问。它允许用户从一个计算机(称为客户端)远程控制另一个计算机(称为服务器),而无需物理上位于服务器旁边。WindowsRDP的底层原理:客户端-服务器架构:客户端:用户......
  • 【Linux】在Ubuntu下开发.Net Framework桌面应用
     一、搭建环境1、VMware安装Ubuntu图文教程https://www.bilibili.com/read/cv25918406/ 2、安装NETSDK(搭建.NETCore环境)终端执行:wgethttps://dot.net/v1/dotnet-install.sh-Odotnet-install.shchmod+x./dotnet-install.sh./dotnet-install.sh--channel6.0......
  • 从星载到桌面:Python带你玩转气溶胶数据处理
    在当前全球气候变化和环境污染问题日益突出的背景下,气溶胶研究显得尤为重要。气溶胶在大气中由直径范围在0.01微米至10微米固体和液体颗粒构成,直接或间接影响地球辐射平衡、气候变化和空气质量。尤其在“碳中和”目标的驱动下,研究气溶胶对“碳中和”的气候影响及其环境效应,不仅......
  • BPI-M4 Berry自动登陆及关闭桌面
    3-1BPI-M4Berry自动登陆及关闭桌面此方法仅适用BPI-M4Berry的ubuntu和debian系统系统默认登录账号,密码当开发板启动完成,输入账号密码即可登录。由BananaPi提供的镜像,默认账号/密码分别是root/bananapi和pi/bananapi。(输入密码时是不可见的,输入时注意别输入大写字母)Linux终端自......
  • DevExpress WinForms磁贴导航面板 & TileBar组件,让桌面应用触摸更友好!
    界面控件DevExpressWinFormsTileNavPane被设计为位于应用程序窗口的顶部(就像Ribbon一样),可以被认为是Windows桌面应用程序中传统导航元素的触摸友好版本。P.S:DevExpressWinForms拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForms能......
  • Python实战,桌面小游戏,剪刀石头布
    注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。下载教程:Python项目开发实战_桌面小游戏-剪刀石头布_编程案例解析实例详解课程教程.pdf创建一个基于Python的桌面小游戏“剪刀石头布”是一个很好的编程实践项目,它......
  • 【pytest】失败用例,桌面截图
    @pytest.hookimpl(tryfirst=True,hookwrapper=True)defpytest_runtest_makereport(item,call):#executeallotherhookstoobtainthereportobjectoutcome=yieldrep=outcome.get_result()#rep可以拿到用例的执行结果详情ifrep.when=="ca......
  • 使用Vue 2 + Element UI搭建后台管理系统框架实战教程
    后台管理系统作为企业内部的核心业务平台,其界面的易用性和功能性至关重要。Vue2作为一个成熟的前端框架,以其轻量级和高效著称,而ElementUI则是一套专为桌面端设计的Vue2组件库,它提供了丰富的UI元素和组件,大大简化了后台管理系统的开发过程。本篇博客将深入介绍如何利用Vue2......
  • ONLYOFFICE8.1版本桌面编辑器的测评(您的私人办公室)
    ONLYOFFICE官网链接:ONLYOFFICE-企业在线办公应用软件|ONLYOFFICE在线PDF查看器和转换器|ONLYOFFICE​​​​​​在线办公套件|ONLYOFFICE一,引言在数字化浪潮中,高效、便捷、安全的办公工具对现代职场至关重要。今天,我要推荐一款备受好评的在线办公软件——ONLYO......
  • vue-element-admin搭建步骤
    克隆项目gitclonehttps://github.com/PanJiaChen/vue-admin-template.git进入项目目录cdvue-admin-template安装依赖npminstall--registry=https://registry.npm.taobao.org启动服务npmrundev浏览器访问 http://localhost:9528发布构建测试环境npmrun......