uni-app--》基于小程序开发的电商平台项目实战(三)

news2024/11/17 11:45:21

🏍️作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生

🛵个人主页:亦世凡华、

🛺系列专栏:uni-app

🚲座右铭:人生亦可燃烧,亦可腐败,我愿燃烧,耗尽所有光芒。

👀引言

        ⚓经过web前端的学习,相信大家对于前端开发有了一定深入的了解,今天我开设了uni-app专栏,主要想从移动端开发方向进一步发展,而对于我来说写移动端博文的第二站就是uni-app开发,希望看到我文章的朋友能对你有所帮助。

今天开始使用 vue3 + uni-app 搭建一个电商购物的小程序,因为文章会将项目的每一个地方代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的GitHub上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多前端uni-app知识。然后开篇先简单介绍一下本项目用到的技术栈都有哪几个方面(阅读此次项目实践文章能够学习到的技术):

uni-app:跨平台的应用开发框架,基于vue.js可以一套代码同时构建运行在多个平台。

pnpm:高性能、轻量级npm替代品,帮助开发人员更加高效地处理应用程序的依赖关系。

vue3:vue.js最新版本的用于构建用户界面的渐进式JavaScript框架。

typescript:JavaScript的超集,提供了静态类型检查,使得代码更加健壮。

pinia:vue3构建的Vuex替代品,具有响应式能力,提供非常简单的 API,进行状态管理。

uni-ui:基于vue.js和uni-app的前端UI组件库,开发人员可以快速地构建跨平台应用程序。

如果是第一次接触uni-app并且想学习uni-app的朋友,我是不建议直接从此次实战项目开始看起,可以先阅读一下我以前的基础文章:什么是uniapp?如何开发uniapp?按部就班的学习可以让学习变得更轻松更容易上手哦,闲话少说我们直接开始今天的uni-app实战篇。

目录

实现上拉触底

实现下拉刷新

生成骨架屏

热门推荐详情实现

分页条件设置


实现上拉触底

在上一章节我们已经实现首页静态模块的搭建,接下来我们需要加载猜你喜欢的更多相关的数据,这里需要借助上拉触底的功能,因为滚动容器内包裹着猜你喜欢的组件,所以我们还需要通过父调子的方法来拿到相应的组件实例,这里我们先实现上拉触底的具体操作:

这里我们需要在滚动容器当中添加 scrolltolower 函数来触发上拉触底的事件:

接下来我们需要在调用上拉触底事件的时候,调用猜你喜欢组件内容的方法,这里需要我们将猜你喜欢的组件调用数据方法先暴露出来:

然后通过 InstanceType<typeof Swiper> 用于获取 Swiper 这个类的实例类型。 

// 组件实例类型
export type SwGuessInstance = InstanceType<typeof SwGuess>

然后通过ref获取 SwGuess 组件的相应实例,之后便可以调用SwGuess组件当中的方法:

// 滚动触底事件函数
let guessRef = ref<SwGuessInstance>() // 获取猜你组件实例
const onScrolltolower = () => {
  guessRef.value?.getGuess()
}

父组件调用子组件获取猜你喜欢函数的方法之后,当我们进行上拉触底的时候,该函数就会再次杯执行,如下:

接下来对猜你喜欢的接口函数进行修改,我们之前编写的接口仅仅是获取默认第一页的数据而已,这里我们还需要传入可选参数页码page和页数pageSize,如下:

/** 通用分页参数类型 */
export type PageParams = {
  /** 页码:默认值为 1 */
  page?: number
  /** 页大小:默认值为 10 */
  pageSize?: number
}

接下来我们就需要对猜你喜欢的接口函数进行相应的修改了:

/* *
 * 获取首页猜你喜欢的接口函数
 */
export const getHomeGoodsGuessLike = (data?: PageParams) => {
  return http<PageResult<GuessItem>>({
    method: 'GET',
    url: '/home/goods/guessLike',
    data,
  })
}

编写完相应的接口函数之后,接下来我们需要在相应的猜你喜欢的组件中对猜你喜欢的接口函数传入相应的参数数据,然后对相应的数据进行一个数组的push,然后在进行一个页码数据的累加,因为页码的接口类型是可选参数,直接进行累加的话会ts类型报错,这里我们需要将可选参数变为必选参数,所以这里使用了必须类型的Required泛型方式,如下:

// 获取猜你喜欢数据
let pageParmas: Required<PageParams> = {
  page: 1,
  pageSize: 10,
}
let guessList = ref<GuessItem[]>([]) // 猜你喜欢数据列表
const getHomeGoodsGuessLikeData = async () => {
  const res = await getHomeGoodsGuessLike(pageParmas)
  // guessList.value = res.result.items
  guessList.value.push(...res.result.items)
  // 页码累加
  pageParmas.page++
}

这里我们还需要对分页进行一个条件的判断,因为数据可能是有限的,如果我们不加以限制的话数据可能就会一直无限的循环下去,这里需要我们进行数据总数的一个判断,其对应的逻辑如下:

这里通过设置一个结束标记来判断当前的页码是否小于总页码数,如果已经或等于的话,这里就借助uni-app提供的一个弹出消息框来提醒用户,当前的数据已经全部加载完毕了:

// 设置一个结束标记
let finish = ref<boolean>(false)
let guessList = ref<GuessItem[]>([]) // 猜你喜欢数据列表
const getHomeGoodsGuessLikeData = async () => {
  if (finish.value) {
    return uni.showToast({
      title: '没有更多数据了~',
      icon: 'none',
    })
  }
  const res = await getHomeGoodsGuessLike(pageParmas)
  // guessList.value = res.result.items
  guessList.value.push(...res.result.items)
  if (pageParmas.page < res.result.pages) {
    // 页码累加
    pageParmas.page++
  } else {
    finish.value = true
  }
}

呈现的效果如下:

实现下拉刷新

实现下拉刷新需要借助的 refresher-enabled 属性来实现,其默认为false,这里我们在滚动容器中填写该属性即可,然后再通过自定义下拉刷新函数来设置相应的规则:

自定义下拉刷新函数,这里我们把首页每个模块获取到的数据的函数再重新的调用一下:

// 自定义下拉刷新
const onRefresherrefresh = () => {
  getHomeBannerData() // 轮播图数据
  getHomeCategoryData() // 分类数据
  getHomeHotData() // 猜你喜欢数据
}

最终呈现的结果如下:

虽然我们实现了下拉刷新后,数据的重新加载变化,但是下拉刷新的状态还是存在一直没消失,这里我们需要对其进行处理一下,通过 refresher-triggered 属性设置其下拉刷新的状态,如下:

然后通过函数的调用时机,巧妙在数据加载完成之后再关闭下拉刷新的动画效果:

// 设置下拉刷新状态
let isTriggered = ref<boolean>(false)
// 自定义下拉刷新
const onRefresherrefresh = async () => {
  // 开启动画
  isTriggered.value = true
  // 加载数据
  await getHomeBannerData() // 轮播图数据
  await getHomeCategoryData() // 分类数据
  await getHomeHotData() // 猜你喜欢数据
  // 加载数据完成关闭动画
  isTriggered.value = false
}

虽然我们实现了下拉刷新的效果,但是这个存在着一个性能问题,就是需要按个等每个数据都加载完成之后才会结束下拉刷新的效果:

为了避免这个问题,我们可以借助es6语法的Promise.all()方法,等所有异步函数都加载完才执行

// 设置下拉刷新状态
let isTriggered = ref<boolean>(false)
// 自定义下拉刷新
const onRefresherrefresh = async () => {
  // 开启动画
  isTriggered.value = true
  // 加载数据
  // await getHomeBannerData() // 轮播图数据
  // await getHomeCategoryData() // 分类数据
  // await getHomeHotData() // 猜你喜欢数据
  await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()])
  // 加载数据完成关闭动画
  isTriggered.value = false
}

这样的方式就会很快结束我们下拉刷新的进程,提高了资源的利用效率:

接下来我们还需要处理猜你喜欢组件下拉刷新时更新数据的方法,这里我们需要在猜你组件当中对我们当前的页码数据列表以及相应的结束标记都需要进行一个相应的重置,然后将我们重置的方法对外暴露出去:

然后再在下拉刷新函数加载数据处,先调用猜你喜欢重置函数,然后再调用获取数据函数,这样的话当我们下拉查看到大于1的页码猜你喜欢的数据之后,回到首页再下拉刷新一下,猜你喜欢的数据又会重新加载到第一页:

生成骨架屏

骨架屏(Skeleton Screen)是一种应用于移动端和网页端的用户体验优化策略。它是在页面或者应用还未加载完毕时,先展示一个大致布局结构相同、内容却尚未加载完成的界面UI效果,让用户感知到应用正在加载中并且保证用户对内容的期待。之后再逐渐替换成真实数据。

在微信开发者工具当中已经帮助我们提供了一键生成骨架屏的按钮,在模拟器的右下方就有按钮:

我们点击生成骨架屏之后会生成相应的代码文件,这里我们需要将微信开发者工具生成的wxml和wxss文件转换成vue文件即可:

接下来我们将生成好的vue文件导入到首页当中,然后通过v-if和v-else进行相应的判断

我们在页面挂载的时候设置一个标记,开始时处于骨架屏状态,当页面同时都加载完成之后,接下来我们就可以将标记赋值为false然后关闭骨架屏展示相应的页面内容:

// 标记是否在加载中
let isLoading = ref<boolean>(false)
// 组件刚加载的时候调用
onLoad(async () => {
  // 页码处于加载中
  isLoading.value = true
  await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()])
  // 页面加载完毕
  isLoading.value = false
})

这里我们将微信开发者工具的网速调低,方便我们清晰的看到初始时骨架屏加载后的效果:

热门推荐详情实现

在之前我们已经实现了首页四个热门推荐详情数据的静态展示,接下来我们实现点击热门推荐进行页面跳转,实现热门推荐具体详情数据的展示,首先我们先在pages文件夹下新建uniapp页面,该页面会自动加载到我们的pages.json里面:

页面新建好之后,接下来我们开始实现路由的跳转,因为热门推荐在首页的展示也是通过组件进行展示的,所以我们只要在热门推荐相关书写的组件中进行设置路由的跳转即可,这里我们借助uniapp中navigator固有的语法进行设置即可,具体的属性讲解这里就不再赘述了,想了解的朋友可以随时去官网进行查看,这里仅仅是简单提一下我们目前所使用的方式是什么:

根据官方文档给我们提供的相关属性,这里我们就直接使用,并且巧妙的借助模板字符串进行动态的路由传参,将接口数据中特别分类热门推荐的数据type进行传递过去,便于区分我们点击的是谁

接下来我们开始编写获取热门推荐相关数据的接口函数,这里函数设置的传递参数有两个,url路径是必传参数,data为可选参数为了后面设置分页数据加载做准备:

import type { PageParams } from '@/types/global'
import type { HotResult } from '@/types/hot'
import { http } from '@/utils/http'

type HotParams = PageParams & { subType?: string }
// 热门推荐相关数据展示
export const getHotRecommendAPI = (url: string, data?: HotParams) => {
  return http<HotResult>({
    method: 'GET',
    url,
    data,
  })
}

编写完接口函数之后,这里我们在我们新建的hot.vue页面进行接口函数的调用,然后通过ref响应式数据进行数据赋值:

<script setup lang="ts">
import { ref } from 'vue'
import { getHotRecommendAPI } from '@/api/home/hot'
import { onLoad } from '@dcloudio/uni-app'
import type { SubTypeItem } from '@/types/hot'

// 热门推荐页 标题和url
const hotMap = [
  { type: '1', title: '特惠推荐', url: '/hot/preference' },
  { type: '2', title: '爆款推荐', url: '/hot/inVogue' },
  { type: '3', title: '一站买全', url: '/hot/oneStop' },
  { type: '4', title: '新鲜好物', url: '/hot/new' },
]
// uni-app 获取页面参数
const query = defineProps<{
  type: string
}>()
const currUrlMap = hotMap.find((v) => v.type === query.type)
// 动态设置标题
uni.setNavigationBarTitle({ title: currUrlMap!.title })

// 推荐封面图
const bannerPicture = ref<string>('')
// 推荐选项
const subTypes = ref<SubTypeItem[]>([])
// 高亮的下标
const activeIndex = ref<number>(0)
// 获取热门推荐数据
const getHotRecommendData = async () => {
  const res = await getHotRecommendAPI(currUrlMap!.url)
  bannerPicture.value = res.result.bannerPicture
  subTypes.value = res.result.subTypes
}
// 页面加载
onLoad(() => {
  getHotRecommendData()
})
</script>

具体的页面布局设置如下,也是非常简单的动态绑定数据然后通过插值表达式进行数据的展示即可,这里简单提一下,我们在上面设置了高亮下标的标记,通过我们点击选项和我们当前的下标进行对比来展示不同的样式和不同的热门推荐数据,如下:

<template>
  <view class="viewport">
    <!-- 推荐封面图 -->
    <view class="cover">
      <image class="imgae" :src="bannerPicture"></image>
    </view>
    <!-- 推荐选项 -->
    <view class="tabs">
      <text
        v-for="(item, index) in subTypes"
        :key="item.id"
        class="text"
        :class="{ active: index === activeIndex }"
        @tap="activeIndex = index"
      >
        {{ item.title }}
      </text>
    </view>
    <!-- 推荐列表 -->
    <scroll-view
      v-for="(item, index) in subTypes"
      :key="item.id"
      v-show="activeIndex === index"
      scroll-y
      class="scroll-view"
    >
      <view class="goods">
        <navigator
          hover-class="none"
          class="navigator"
          v-for="goods in item.goodsItems.items"
          :key="goods.id"
          :url="`/pages/goods/goods?id=${goods.id}`"
        >
          <image class="thumb" :src="goods.picture"></image>
          <view class="name ellipsis">{{ goods.name }}</view>
          <view class="price">
            <text class="symbol">¥</text>
            <text class="number">{{ goods.price }}</text>
          </view>
        </navigator>
      </view>
      <view class="loading-text">正在加载...</view>
    </scroll-view>
  </view>
</template>

最终呈现的效果如下:

分页条件设置

当我们设置完初始的热门推荐详情页面之后,接下来我们需要开始为该详情页面进行设置相应的分页加载数据了,关于分页加载数据的讲解在上一篇文章讲解猜你喜欢数据的时候已经讲解过一遍了,当然这里在简单的进行讲解一下:

首先我们需要先给滚动容器设置滚动触底事件,因为热门推荐详情页面有小tab按钮进行切换展示不同的热门推荐的数据,所以这里我们需要先获取我们点击当前热门推荐详情tab的下标,因为我们之前是通过v-show进行页面的切换展示的,不同的tab按钮展示的数据相互独立不会影响,所以不同担心。

// 获取当前的选项
const currsubTypes = subTypes.value[activeIndex.value]
// 分页条件
if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
  // 当前页码累加
  currsubTypes.goodsItems.page++
} else {
  // 标记已结束
  currsubTypes.finish = true
  // 退出并提示消息
  return uni.showToast({ icon: 'none', title: '没有更多数据了~' })
}

根据条件的判断来确保是否要增加当前页码,然后接下来将增加的页码值再通过热门推荐的接口函数中进行传递再获取当前的数据,将获取到的数据再进行追加到我们设置好的参数中然后再进行调用相应的接口函数再一次获取数据,完整代码如下:

// 自定义滚动触底事件
const onScrolltolower = async () => {
  // 获取当前的选项
  const currsubTypes = subTypes.value[activeIndex.value]
  // 分页条件
  if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
    // 当前页码累加
    currsubTypes.goodsItems.page++
  } else {
    // 标记已结束
    currsubTypes.finish = true
    // 退出并提示消息
    return uni.showToast({ icon: 'none', title: '没有更多数据了~' })
  }

  // 调用API传参
  const res = await getHotRecommendAPI(currUrlMap!.url, {
    subType: currsubTypes.id,
    page: currsubTypes.goodsItems.page,
    pageSize: currsubTypes.goodsItems.pageSize,
  })
  // 新的列表选项
  const newsubTypes = res.result.subTypes[activeIndex.value]
  // 数据追加
  currsubTypes.goodsItems.items.push(...newsubTypes.goodsItems.items)
}

这里我们设置了一个结束标记finish,用来判断当前的数据是否已经加载完毕,因为subTypes本身是没有这个ts类型的,所以这里我们在之前设置ts类型基础之上,再添加一个:

// 推荐选项
const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([])

然后这里我们就可以通过三元表达式进行判断当前的数据是否已经加载完毕了:

<view class="loading-text">{{ item.finish ? '没有更多数据了~' : '正在加载...' }}</view>

这里还有一个小技巧,因为我们目前的分页数据有很多,当我们进行测试的时候需要不停的滚动数据,这里只要我们通过判断当前是否是开发还是生产环境,来动态的改变我们当前初始的页码值就可以方便的进行我们数据的测试了:

如果是开发环境,测试页码是30否则就是1,所以当我们进行开发的时候就会从30页开始,打包上线之后,我们的初始页码数据就会从1进行开始了:

最终呈现的效果如下:

本项目首页的一些基本功能的搭建就讲解到这,下一篇文章将继续讲解项目的分类页码代码操作,关注博主学习更多前端uni-app知识,您的支持就是博主创作的最大动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1047124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

较真儿学源码系列-PowerJob MapReduce源码分析

PowerJob版本&#xff1a;4.3.2-main。 之前分析过PowerJob的时间轮源码&#xff0c;感兴趣的可以查看《较真儿学源码系列-PowerJob时间轮源码分析》 1 简介 MapReduce是一种编程模型&#xff0c;以及在集群上使用并行、分布式算法处理和生成大数据集的相关实现。 一个…

MySQL学习笔记18

MySQL的备份与恢复&#xff1a; 制定数据库备份策略进行备份&#xff0c;并且把数据导入到测试环境。 核心技术&#xff1a; 1&#xff09;掌握MySQL的备份工具使用及各自特点&#xff1b; 2&#xff09;熟悉Shell脚本&#xff1b; 3&#xff09;熟悉MySQL数据的导入导出&a…

苹果 CMS 原生 Java 白菜影视 App 源码【带打包教程】

苹果 CMS 原生 Java 白菜影视 App 源码是一款功能强大的影视应用程序&#xff0c;支持画中画、投屏、点播、播放前广告和支持普通解析等多种功能。与萝卜 App 源码相比&#xff0c;该套源码更加稳定&#xff0c;且拥有画中画投屏和自定义广告等功能&#xff0c;提高了安全性。 …

PMP考前学习计划

很多小伙伴在刚刚接触到PMBOK时&#xff0c;无从下手&#xff0c;也不知道如何合理地安排自己的学习时间&#xff0c;没有一个学习计划作为指导。 今天我就给大家分享一份详细的PMP考前学习计划&#xff0c;这份计划整理并无私分享&#xff0c;欢迎大家分享给身边备考PMP的同事…

Java分支结构:一次不经意的选择,改变了我的一生。

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、顺序结构二、分支结构1、if语句2、switch语句 好久不见&#xff01;命运之轮常常在不经意间转动&#xff0c;有时一个看似微…

SEO的优化教程(百度SEO的介绍和优化)

百度SEO关键字介绍&#xff1a; 百度SEO关键字是指用户在搜索引擎上输入的词语&#xff0c;是搜索引擎了解网站内容和相关性的重要因素。百度SEO关键字可以分为短尾词、中尾词和长尾词&#xff0c;其中长尾词更具有针对性和精准性&#xff0c;更易于获得高质量的流量。蘑菇号-…

【MATLAB源码-第39期】基于m序列/gold序列的直接扩频通信仿真,编码方式采用卷积码,调制方式采用BPSK。

1、算法描述 直接序列扩频通信系统的仿真一般包括以下几个主要步骤&#xff1a;信号产生、扩频、卷积编码、BPSK调制、信道传输、BPSK解调、卷积码译码和解扩。 信号产生&#xff1a; 首先&#xff0c;产生一个二进制数据序列作为待发送的信息位。 扩频&#xff1a; 采用m序列…

windows11系统没有系统散热方式的解决办法

一、问题描述 当我们查看Win11系统的&#xff08;同时按下键盘的WinR键即可打开运行窗口&#xff09;【控制面板】-->【硬件和声音】-->【电源选项】-->【更改计划设置】-->【 更改高级电源设置】-->【处理器电源管理】下没有系统散热方式的选项&#xff0c;如下…

黑马JVM总结(二十五)

&#xff08;1&#xff09;字节码指令-cinit 构造方法可以分为两类&#xff0c;一类是cinit 一类init cinit是整个类的构造方法 putstatic&#xff1a;进行static变量的赋值&#xff0c;是到常量池里找到名字一个叫做i的变量 &#xff08;2&#xff09;字节码指令-init in…

Anchors

这是源代码定义的anchors概念&#xff1a; 实现过程&#xff1a; 假如有一张500500的图片&#xff0c;那么经过第一步深度卷积网络之后&#xff08;4次池化&#xff09;&#xff0c;最终就会变成一个3232的特征&#xff1a; 在开源代码实现里面&#xff1a; 所以经过卷积完之后…

word中给公式加序号的方法

①首先&#xff0c;用word插入一个公式 然后呢&#xff0c;在公式后面敲上这个公式在整篇文章中的序号。我的这个公式在整篇文章中是第三号&#xff0c;所以就敲上(3),如下图所示&#xff1a; 然后&#xff0c;在公式和序号之间&#xff0c;按住shift3(#) 切忌&#xff0c;…

makdown文法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

【Win11 搭建miniconda 的pytorch1.12环境】

请不要质疑我一直在水文章&#xff0c;因为我电脑被格式化了&#xff0c;需求又变了&#xff0c;这不得多多与时代接轨哦&#xff01; 为我的GRCNN抓取打基础&#xff0c;之前是在Ubuntu上跑&#xff1a;【机械臂视觉抓取从理论到实战】&#xff0c;没错现在就是在WIN11上跑&am…

《数据结构、算法与应用C++语言描述》使用C++语言实现数组栈

《数据结构、算法与应用C语言描述》使用C语言实现数组栈 定义 栈的定义 把线性表的插入和删除操作限制在同一端进行&#xff0c;就得到栈数据结构。因此&#xff0c;栈是一个后进先出&#xff08;last-in-first-out&#xff0c;LIFO&#xff09;的数据结构。 栈&#xff08…

测试用例的八大基本准则

测试用例的八大基本准则 测试用例的八大基本准则功能测试性能测试兼容性测试安全测试可靠性测试易用性测试数据库测试接口测试 测试案例 测试用例的八大基本准则 上节测试用例的设计中我们讨论如何设计一个测试用例&#xff0c;知道了测试用例的设计有&#xff1a;“边界值&am…

【搭建yolox深度学习环境】

这里写目录标题 一、环境配置二、安装所需库文件2.1 安装apex 一、环境配置 首先进行yolox模型的下载&#xff1a;YOLOX(gitcode) 或者YOLOX(github) 并选择自己所需的权重模型&#xff0c;如-s-m-l等&#xff08;.pth文件&#xff09; 如果需要进行labview的使用&#xff0c;…

正则表达式贪婪模式和非贪婪模式

一、贪婪模式 贪婪模式表示尽可能多的匹配字符串&#xff0c;正则表达式六个量词元字符?、、*、{n}、{n,m}、{n,}默认是贪婪模式 接下来引入一个场景来分析说明 获取html a标签href属性的值 <a href"https://www.baidu.com/" attr"abc"></a>…

可控情感的表现力语音驱动面部动画合成

高度逼真的面部动画生成需求量很大&#xff0c;但目前仍然是一项具有挑战性的任务。现有的语音驱动面部动画方法可以产生令人满意的口部运动和嘴唇同步&#xff0c;但在表现力情感表达和情感控制的灵活性方面仍存在不足。本文提出了一种基于深度学习的新方法&#xff0c;用于从…

stm32之1602+DHT11+继电器

描述&#xff1a; 1、DHT11监测温室度&#xff0c;并显示到1602液晶上 2、通过串口打印&#xff08;或通过蓝牙模块在手机上查看&#xff09; 3、当温度大于24度时&#xff0c;开启继电器。小于时关闭继电器&#xff08;继电器可连接风扇---假想O(∩_∩)O哈哈~&#xff09; 一、…

【Axure教程】用中继器制作双坐标柱状折线图

双坐标柱状折线图常用于同时展示两组数据的图表类型&#xff0c;每组数据都有自己的纵坐标轴&#xff08;Y轴&#xff09;。一组数据通常用柱状图表示&#xff0c;而另一组数据则用折线图表示。这种图表类型有助于比较两组数据之间的关系和趋势。 那今天作者就教大家&#xff…