electron-updater实现electron全量更新和增量更新——渲染进程UI部分

同学们可以私信我加入学习群!


正文开始


前言

更新功能的实现分为三篇文章来讲解:

1.主进程更新

2.开发调试技巧

3.渲染进程部分。

我在开发过程中,是先开发的主进程主要功能,再完善渲染进程部分的显示。因为当没有前端时,可以写几个简单的标签显示结果,保证主要功能基本完成,再完善前端交互。

效果图如下:

上面有几个简单的交互:

  • 打开软件时,页面向主进程通信,查询是否更新
  • 发现需要更新,弹出一个更新交互页面,页面显示更新信息和更新操作
  • 更新时,点击关闭按钮,会进入后台更新模式,登录进去后,点击左上角的更新标志,会弹出更新页面。

大的需求就是这样三个,至于细节后文再展开叙述,比如跳过版本如何实现,如何保障登录页和登录后,更新的进度是保持一致的......

更新功能所有文章汇总

  1. electron-updater实现electron全量更新和增量更新------主进程部分
  2. electron-updater实现electron全量更新和增量更新------注意事项/技巧汇总
  3. electron-updater实现electron全量更新和增量更新------渲染进程UI部分
  4. electron-updater实现electron全量更新和增量更新------渲染进程交互部分

一、两个同心球效果实现

在讲解前,我们先整体了解一下前端的文件结构:

  • updateprogress.vue是唯一的vue文件
  • updateBall.js:和中间的球相关的逻辑
  • updateHandle.js:操作按钮相关的逻辑
  • store/update.js:更新模块的全局变量

静态页面部分没什么好说的,就是一个遮罩加两个球,还有一些按钮,我的审美并不好,大家可以自行设计。球的立体效果就是靠阴影效果,代码如下:

c 复制代码
<div
    :style="{ width: ballRadius * 2 + 'px', height: ballRadius * 2 + 'px' }"
    class="ball">
      
</div>

<style scope>
.ball {
  border-radius: 50%;
  //background: linear-gradient(135deg, #fff, #ddd);
  background-color: #fff;
  box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .1),
  0 0 10px 2px rgba(0, 0, 0, .1);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  flex-direction: column;
}

.ball::before {
  content: "";
  width: 80%;
  height: 80%;
  border-radius: 50%;
  //background: linear-gradient(315deg, #ddd, #fff);
  background-color: #fff;
  box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .1),
  0 0 10px 2px rgba(0, 0, 0, .1);
  position: absolute;
}
</style>

类ball是外层的大球,设置的半径ballRadius 为100,伪类:before是小球,大球小球靠阴影效果,塑造立体感。

二、球内进度条、logo、粒子元素实现

2.1 球内包含几个元素:

  • logo
  • 不规则的粒子
  • 进度条

外层的ball采用的flex布局,布局方向是纵向,所以logo通过图片标签直接引入,设置好宽高即可,下面依次写进度条、粒子等元素:

c 复制代码
 <div :style="{width:ballRadius*2+'px',height:ballRadius*2+'px'}" class="ball">
      <img alt="Logo" class="logo" src="/public/img/log-opacity.png">
      <div style="display: flex;flex-direction: column;width: 60%">
        <Progress :percent="update_info.percent || 0" :stroke-width="5" style="flex-grow: 1;margin-right: 4px">
          <div style="width: 100%;display: flex;flex-direction: row;justify-content: center;align-content: center">
            <span>{{ update_info.percent || 0 }}%</span>
            <span style="color: #1a1a1a;margin-left: 8px">{{ update_info.speed || '0kb' }}</span>
          </div>
        </Progress>
      </div>
      <div class="particles">
        <!--        循环渲染粒子-->
        <div v-for="(particleItem,index) in particlesList"
             :key="index"
             :ref="el => setItemRef(index, el)"
             :style="{left: particleItem.left+'px',top:particleItem.top+'px',backgroundColor:particleItem.color}"
             class="particle">
        </div>
      </div>
    </div>

2.2 随机粒子生成方法generateRandomPoint

重点讲解一下上面的粒子渲染。

首先,粒子的容器是和最大的球重合的,所以采用absolute定位,css代码如下:

c 复制代码
.particles {
  position: absolute;
  width: 100%;
  height: 100%;
}

粒子渲染最重要的是随机在圆环位置生成粒子的逻辑,这部分的算法核心其实就是初中数学知识:

  1. 外球半径(outRadius)-内球半径(radius)=圆环半径
  2. 圆环半径*随机数+内球半径(radius)=落在圆环内的随机半径
  3. 落在圆环内的随机半径*角度的cos和sin值,就分别是随机半径终点的坐标

转化为代码就是:

c 复制代码
//生成radius外,outRadius内圆环上的坐标
      const randomRadius = radius + Math.random() * (outRadius - radius)
        const outX = randomRadius * Math.cos(angle)
        const outY = randomRadius * Math.sin(angle)

上面的代码就会生成angle角度方向,不同半径的坐标,如果angle角度也是随机的,那么就会生成圆环内随机散布的粒子

c 复制代码
const angle = Math.random() * (2 * Math.PI)
      const randomRadius = radius + Math.random() * (outRadius - radius)
        const outX = randomRadius * Math.cos(angle)
        const outY = randomRadius * Math.sin(angle)

然后根据这个算法,我又增加了两种场景,一种是随机粒子是在内球半径上,得到粒子的坐标,思路是一样的,就不再赘述:

c 复制代码
        //生成半径radius上的坐标

        const innerX = Math.random() * radius * Math.cos(angle)
        const innerY = Math.random() * radius * Math.sin(angle)

另一种是随机粒子是在内球半径内,也就是粒子随机散布在小球内:

c 复制代码
       //生成半径radius内的坐标

        const innerX = Math.random() * radius * Math.cos(angle)
        const innerY = Math.random() * radius * Math.sin(angle)

最终这个方法就是:

c 复制代码
 function generateRandomPoint(radius, outRadius) {
        //根据半径生成随机坐标,radiusArr:半径上的坐标,outRadiusArr:半径外的坐标,innerRadiusArr:半径内的坐标
        //outRadiusArr只有存在外环半径outRadius时才会生成
        // 生成一个0到2π之间的随机角度
        const angle = Math.random() * (2 * Math.PI)

        // 极坐标到笛卡尔坐标的转换,生成半径radius上的坐标
        const x = radius * Math.cos(angle)
        const y = radius * Math.sin(angle)
        //生成半径radius内的坐标

        const innerX = Math.random() * radius * Math.cos(angle)
        const innerY = Math.random() * radius * Math.sin(angle)

        //生成radius外,outRadius内的坐标
        // debugger
        const randomRadius = radius + Math.random() * (outRadius - radius)
        const outX = randomRadius * Math.cos(angle)
        const outY = randomRadius * Math.sin(angle)


        return {
            radiusArr: [x, y],
            innerRadiusArr: [innerX, innerY],
            outRadiusArr: [outX, outY]
        }
    }

本文的设计是将粒子散布在圆环内,所以只需要返回值的outRadiusArr参数即可。

2.3 创建多个粒子的方法createParticle

通过上面的generateRandomPoint方法,我们可以得到粒子的一个随机坐标。只要我们循环调用该方法,那就会循环得到粒子的不同坐标,代码如下:

c 复制代码
    function createParticle(num) {
        // debugger
//  根据num生成随机粒子
        for (let i = 0; i < num; i++) {
            const {
                outRadiusArr: [outX, outY]
            } = generateRandomPoint(70, ballRadius.value)
            const particle = {
                color: getRandomRGBColor(),
                left: 100 + outX,
                top: 100 + outY,
            }
            particlesList.value.push(particle)
        }
    }

其中的getRandomRGBColor方法是获取随机颜色的方法:

c 复制代码
    function getRandomRGBColor() {
        // 限制绿色和蓝色分量在100到255之间,红色分量在0到100之间
        const r = Math.floor(Math.random() * 101) // 0 to 100
        const g = Math.floor(Math.random() * 156) + 100 // 100 to 255
        const b = Math.floor(Math.random() * 156) + 100 // 100 to 255
        return `rgb(${r}, ${g}, ${b})`
    }

最终使用vue的v-for将粒子循环渲染到页面。

三、gsap创建路径动画,实现粒子动画

使用gsap插件,可以很方便地实现元素围绕某个路径运动。不仅限于圆弧,还可以是曲线、折线、不规则图形,gsap是flash基于js上的实现,功能十分强大,有兴趣的同学可以查看往期博文,这里不重点阐述概念。

创建动画的方法如下:

c 复制代码
    function createAnimation(movementRange = 3) {
        // 使用GSAP创建动画
        particlesList.value.forEach((particle, index) => {
            // 使用GSAP创建动画
            gsap.to(particleRefs.value[index], {
                motionPath: {
                    path: '#svg',
                    align: '#svg',
                    alignOrigin: [Math.random() * 10 - 5, Math.random() * 10 - 5]
                },
                repeat: -1, // 无限重复
                duration: 3 * Math.random() + 2, // 随机持续时间

                ease: 'linear', // 线性运动
                delay: Math.random() * 2 // 随机延迟
            })
        })
    }

这里有几个小技巧:

  1. 仔细的同学可以发现,在vue中,粒子的ref变量是通过setItemRef方法实现的
c 复制代码
	//vue中的代码
        <div v-for="(particleItem,index) in particlesList"
             :key="index"
             :ref="el => setItemRef(index, el)"
             :style="{left: particleItem.left+'px',top:particleItem.top+'px',backgroundColor:particleItem.color}"
             class="particle">
        </div>

	//对应的js代码     
    function setItemRef(index, el) {
        if (el) {
            // 如果元素存在,则将其存储在对象中
            particleRefs.value[index] = el
        } else {
            // 如果元素不存在(可能是被销毁了),则从对象中删除
            delete particleRefs.value[index]
        }
    }

这个方法最终会得到一个保存粒子ref对象的数组particleRefs。

  1. gsap动画需要指定动画元素, gsap.to的第一个参数particleRefs.valueindex就是循环得到的动画元素,也就是每一个粒子。
  2. gsap的基础配置十分简单,这里主要是讲解motionPath参数。这是路径插件的参数:
c 复制代码
   motionPath: {
                    path: '#svg',
                    align: '#svg',
                    alignOrigin: [Math.random() * 10 - 5, Math.random() * 10 - 5]
                },

前面两个参数好理解,就是粒子绕着路径运动,总得先定义路径,svg就是路径的id。alignOrigin是粒子偏移路径的距离,使用随机数,可以让粒子运动效果有杂乱随机的感觉。

  1. svg是路径path的id,不是svg标签的id,这个要注意,对应的svg代码如下:
c 复制代码
  <!-- SVG 圆形元素 -->
      <svg style="position: absolute" height="95%" viewBox="-160 -160 320 320" width="95%"
           xmlns="http://www.w3.org/2000/svg">
        <path id="svg"
              d="M 0 160
         A 160 160 0 0 1 0 -160
         A 160 160 0 0 1 0 160 Z"
              fill="transparent" stroke="none"/>
      </svg>

构建svg的时候,还要注意原点、是否闭合、起止点等信息,也就是d元素中的数据。如果d属性的路径设置不合理,可能会造成path路径与外部的圆不重合的问题。当然这些一般没人去手输,通过Adobe AI软件、在线svg绘制网站、ai助手等,都可以得到符合要求的svg路径。

对svg不熟悉的同学,要关注viewBox属性,这是svg可以跟随父级容器按照比例增大缩小的关键。

  1. 启动动画:当用户点击立即更新时,需要做三件事:1)创建200个粒子;2)启动动画;3)检查更新。代码如下:
c 复制代码
    function startUpdate() {
        createParticle(200)
        setTimeout(() => {
            createAnimation()
            myApi.handlePcToUpdate()
        }, 100)

    }

总结

本文主要是讲解了更新模块的页面样式实现,下一篇文章讲解页面上的交互逻辑实现。

大家如果需要联系博主,或者获取博主各系列文章对应的资源,可以通过中二少年学编程的个人主页来获取。

有任何前端项目、demo、教程需求,都可以联系博主,博主会视精力更新,免费的羊毛,不薅白不薅!~

相关推荐
AI_零食11 小时前
鸿蒙PC Electron跨平台应用开发:24时区时间表应用详解
前端·华为·electron·开源·harmonyos·鸿蒙
文创工作室14 小时前
Adobe Illustrator 中文
ui·adobe·illustrator
提子拌饭13314 小时前
爆发效果技术——基于鸿蒙PC Electron框架实现
华为·架构·electron·开源·harmonyos·鸿蒙·鸿蒙系统
超梦dasgg20 小时前
详细讲解 AI 上下文(Context)
人工智能·状态模式
烛衔溟21 小时前
HarmonyOS 基础 UI 构建 —— 组件、布局与沉浸式效果
ui·华为·harmonyos
TrisighT1 天前
Electron 的 printToPDF 在鸿蒙 PC 上翻车了,我换了个纯前端方案绕过去
electron·harmonyos
怕浪猫1 天前
Electron 开发实战(十一):自动更新机制|服务架构、公私网更新、版本回滚全解
前端·javascript·electron
一次旅行1 天前
CopilotKit实战:用生成式UI打造智能Agent前端
前端·人工智能·ui
web打印社区1 天前
前端html转换pdf并静默打印pdf最佳实现路径
前端·javascript·vue.js·electron·html
gqk011 天前
Delegate.Target/ Method
前端·ui·xhtml