【组件封装】uniapp vue3 封装一个完整的Tabs(标签页)组件教程,功能由简到杂实现讲解。
文章目录
- 前言
- 一、简单版Tabs
- 代码实现:
- 二、下划线带动画的Tabs
- API回顾:
- 代码实现:
- 三、内容区域滑动切换+切换动画
- 代码实现:
- (2)禁用手势滑动切换
- (3)内容区域换为插槽
- 四、标签栏可滚动
- 代码实现
前言
手把手教你封装一个移动端 Tabs组件(标签页),功能由简到杂以uniapp vue3为代码示例。

一、简单版Tabs
实现一个最简单版的Tabs,下划线无动画无手势切换等,如下图所示:

实现说明:标签通过flex布局排列,下划线通过伪类绝对定位在选中项底部,选中项通过索引记录动态添加激活class
代码实现:
tabs.vue
<template><view class="tabs"><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in list" :key="index"@click="handleSelect(index)">{{item}}</view></view>
</template><script setup>import {computed,ref} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = index}}
</script><style lang="scss" scoped>.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);//下划线&::after {display: block;content: '';position: absolute;bottom: 0;left: 50%;transform: translateX(-50%);width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;}}}}
</style>
ps:注意scss中使用了v-bind引用vue变量activeColor动态设置标签和下划线激活颜色
二、下划线带动画的Tabs
接下来功能升级,要求切换标签的时候下划线有滑动动画,如下图所示

实现说明:因为下划线要有滑动动画,就不能相对于选中项绝对定位,而应该基于一个固定的父级元素定位,这个父元素就是组件最外层容器。选中后通过计算下划线到父容器距离(也就是下划线到页面最左边距离)确定绝对定位的left值,同时设置过渡动画。
API回顾:
在uniapp中由于底层使用引擎不同小程序或者app中无法像h5一样进行任何dom操作,只能通过官方提供的api来获取节点信息。
uni.createSelectorQuery()可用于获取节点信息,并结合如下使用方式:
import { getCurrentInstance } from 'vue';
const instance = getCurrentInstance();const query = uni.createSelectorQuery().in(instance.proxy);
query.select("#id").boundingClientRect((data) => {console.log("得到布局位置信息" + JSON.stringify(data));console.log("节点离页面顶部的距离为" + data.top);console.log("节点离页面左边的距离为" + data.left);}).exec();
来获取节点宽高和距离窗口左边或者顶部距离。
下划线位置计算:

下划线绝对定位left值=a段长度,a=b+标签宽/2,而b为标签与页面左边距离,b和标签宽都可以通过节点信息api获取
ps:因为下划线设置了css属性值 transform: translateX(-50%),向左平移自身一半,所以left值为a
代码实现:
tabs.vue
<template><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance=getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value =`${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})
</script><style lang="scss" scoped>.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}
</style>
ps:下划线绝对定位left值在scss通过 v-bind动态访问vue变量
三、内容区域滑动切换+切换动画
在上述示例基础上继续扩展功能,目标是切换标签页支持内容区域带动画同时内容区域滑动可以切换标签,如下图所示:

实现说明:结合swiper轮播图组件封装,内容区域使用swiper作为父容器,因为swiper支持手势滑动和滑动动画。
代码实现:
tabs.vue
<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="item in list" :key="item"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><view class="main">{{item}}</view></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance = getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value = `${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectIndex.value) {handleSelect(e.detail.current)}}
</script><style lang="scss" scoped>.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;flex-shrink: 0;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;text-align: center;padding: 30rpx;box-sizing: border-box;}}
</style>
页面引用:
index.vue
<template><view class="container"><Tabs :list="list" /></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>
说明:为了使内容区域可滚动,内嵌了scroll-view,而scroll-view需要指定高度,整个组件高度默认继承父元素100%,所以在页面使用tabs组件时父元素必须设置高度。
(2)禁用手势滑动切换
swiper组件有个属性disable-touch用来禁用轮播图触摸操作,该属性只支持App 2.5.5+、H5 2.5.5+、支付宝小程序、抖音小程序与飞书小程序。微信小程序可以通过@touchmove.prevent阻止触摸事件冒泡来阻止页面滑动
代码实现:
<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" disable-touch @change="onSwiperChange"><swiper-item class="swiper-item" v-for="item in list" :key="item" @touchmove.prevent><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><view class="main">{{item}}</view></scroll-view></swiper-item></swiper></view></view>
</template>
运行效果:

(3)内容区域换为插槽
内容区域通过动态插槽提供给页面自定义渲染
核心代码如下:
<!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view>
需要注意的是微信小程序端不支持vue插槽动态命名语法,需要写成双花括号形式。
完整代码:
tabs.vue
<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><view :class="['tab',{active:index==selectIndex}]" v-for="(item,index) in list" :key="index"@click="handleSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectIndex" @change="onSwiperChange"><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {computed,ref,onMounted,nextTick,getCurrentInstance} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//激活颜色const activeColor = computed(() => {return props.activeColor || 'deepskyblue'})//当前选中索引const selectIndex = ref(0)//切换标签const handleSelect = (index) => {if (index !== selectIndex.value) {selectIndex.value = indexsetPosition()}}//下划线离父元素左边距const left = ref(null)//组件实例const instance = getCurrentInstance()//设置下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(data => {//定位距离=选中标签项与左距离+标签宽一半left.value = `${data.left+data.width/2}px`}).exec()})}onMounted(() => {//设置下划线初始位置setPosition()})//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectIndex.value) {handleSelect(e.detail.current)}}
</script><style lang="scss" scoped>.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;display: flex;align-items: center;background-color: #fff;position: relative;flex-shrink: 0;.tab {flex: 1;padding: 25rpx 10rpx;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;color: #333;font-size: 30rpx;position: relative;&.active {font-weight: bold;color: v-bind(activeColor);}}}//下划线.underline {position: absolute;width: 30px;height: 6rpx;background-color: v-bind(activeColor);border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;text-align: center;padding: 30rpx;box-sizing: border-box;}}
</style>
页面调用:
index.vue
<template><view class="container"><Tabs :list="list" ><template #content0>手机</template><template #content1>电脑</template><template #content2>电视机</template><template #content3>洗衣机</template></Tabs></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>
四、标签栏可滚动
最终版——功能继续升级,上述案例都是基于标签比较少的场景下使用,如果标签很多超出屏幕就不再适用,此时标签栏需要支持滚动。
我们目标不仅支持滚动,更友好操作体验还希望实现点击某个标签自动移动到屏幕中间,如下图所示:

实现说明:
1、横向滚动:
布局上标签栏外层使用scroll-view包裹,内层依然使用flex布局,每个标签设置基础宽度,当标签过多总体宽度超出屏幕就出现横向滚动。
2、选中标签移动到屏幕中间:
需要分别计算滚动条位置和下划线位置
(1)滚动条位置
scroll-view 有个scroll-left属性控制滚动条位置,我们只需计算该值即可。

如上图所示,假设点击了热水器标签,热水器标签要移动到屏幕中间,需要平移a段距离,a=c+b/2,其中b为标签自身宽度,c为标签距离页面左边距离-页面宽/2,最终滚动条scrollLeft值=原scrollLeft值+a,所以每次标签切换都要记录计算scrollLeft值,scrollLeft初始值为
0。上述几个值都可以通过节点信息api获取。
需要注意的是如果点击第一或第二个标签或最后一个标签情况是无法使得标签移动到正中间,因为滚动条长度有限,所以在计算scrollLeft值时候需要限制最大值最小值。最小值为0,最大值为滚动条长度-页面宽度,而滚动条长度可以通过如下api获取:
query.select('#scrollview').fields({size: true,scrollOffset: true},(data) => {console.log(data.scrollWidth,'滚动条长度')})
(2)下划线位置
下划线还是和之前的案例一样基于父容器绝对定位,因为滚动条的出现使得父容器不在位于页面最左边,而是滚动条最左边,所以下划线位置还需加上滚动条滚出左边页面区域长度也即scrollLeft值
代码实现
tabs.vue
<template><view class="comp-container"><view class="tabs"><!-- 标签栏 --><scroll-view id="scrollview" :scroll-left="scrollLeft" scroll-x style="width:100%" scroll-with-animation><view class="title-wrap" ><view :class="[selectedIndex===index ?'active':'','title-item']" v-for="(item,index) in list":key="item" @click="onSelect(index)">{{item}}</view><!-- 下划线 --><view :class="['underline',{transition:left!==null}]"></view></view></scroll-view></view><!-- 内容区域 --><view class="content"><swiper class="swiper" :current="selectedIndex" @change="onSwiperChange" ><swiper-item class="swiper-item" v-for="(item,index) in list" :key="index"><scroll-view scroll-y style="height:100%"><!-- 页面内容 --><!-- #ifdef H5 ||APP --><slot :name="`content${index}`"></slot><!--#endif --><!-- #ifdef MP-WEIXIN --><slot name="content{{index}}"></slot><!--#endif --></scroll-view></swiper-item></swiper></view></view>
</template><script setup>import {ref,onMounted,getCurrentInstance,nextTick,computed} from 'vue'const props = defineProps({//标签配置list: {type: Array,default: () => []},//激活颜色activeColor: {type: String,defalut: 'deepskyblue'}})//当前选中索引const selectedIndex = ref(0)//下划线离父元素左边距const left = ref(null)const instance = getCurrentInstance()//窗口宽度const winWidth = uni.getSystemInfoSync().windowWidth//设置滚动条和下划线位置const setPosition = () => {nextTick(() => {let query = uni.createSelectorQuery().in(instance.proxy)query.select(".active").boundingClientRect(async data => {//获取滚动条节点信息let scrollViewInfo= await getScrollViewInfo();//重新获取滚动条scrollLeft值,防止用户手动触发滚动情况下值scrollLeft未及时更新scrollLeft.value=scrollViewInfo.scrollLeftlet offsetLeft = data.leftlet offsetWidth = data.width//设置下划线位置left.value = ((offsetLeft + offsetWidth / 2) + scrollLeft.value) + 'px'//计算滚动条位置let _scrollLeft=scrollLeft.value+ data.left + offsetWidth / 2 - winWidth / 2//限制滚动范围_scrollLeft = Math.max(0, _scrollLeft)//设置滚动条位置scrollLeft.value = Math.min(_scrollLeft, scrollWidth.value - winWidth)}).exec()})}//选中标签监听事件const onSelect = index => {if (index !== selectedIndex.value) {selectedIndex.value = index;setPosition()}}//手势切换回调const onSwiperChange = e => {if (e.detail.current !== selectedIndex.value) {onSelect(e.detail.current)}}//滚动条位置const scrollLeft = ref(0)//滚动条长度const scrollWidth = ref(0)//获取滚动条长度和位置信息const getScrollViewInfo = () => {return new Promise((resolve, reject) => {let query = uni.createSelectorQuery().in(instance.proxy)query.select('#scrollview').fields({size: true,scrollOffset: true,},(data) => {resolve({scrollWidth:data.scrollWidth,scrollLeft:data.scrollLeft})}).exec()})}onMounted(() => {nextTick(async () => {//初始化化记录滚动条长度let res= await getScrollViewInfo();scrollWidth.value=res.scrollWidthsetPosition()})})
</script><style lang="scss" scoped>:deep(::-webkit-scrollbar) {display: none;}.comp-container {height: 100%;display: flex;flex-direction: column;background-color: #f2f2f2;overflow: hidden;}.tabs {width: 100%;position: relative;flex-shrink: 0;background-color: #fff;.title-wrap {width: 100%;display: flex;align-items: center;box-sizing: border-box;justify-content: flex-start;position: relative;.title-item {padding: 25rpx 10rpx;flex: 1 0 22%;width: 0;box-sizing: border-box;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;&.active {color: deepskyblue;font-weight: bold;}}}.underline {position: absolute;width: 30px;height: 6rpx;background-color: deepskyblue;border-radius: 6rpx;bottom: 0;left: v-bind(left);transform: translateX(-50%);display: none;&.transition {display: block;transition: all 0.3s;}}}.content {flex: 1;height: 0;overflow: hidden;.swiper {height: 100%;.swiper-item {height: 100%;}}.main {background: #f2f2f2;padding: 30rpx;text-align: center;}}
</style>
页面调用
index.vue
<template><view class="container"><Tabs :list="list"><template #content0>手机</template><template #content1>电脑</template><template #content2>电视机</template><template #content3>洗衣机</template><template #content4>洗碗机</template><template #content5>热水器</template><template #content6>电冰箱</template><template #content7>烤箱</template></Tabs></view>
</template><script setup>import Tabs from '@/components/tabs.vue'import {ref} from 'vue'const list = ref(['手机', '电脑', '电视机', '洗衣机', '洗碗机', '热水器', '电冰箱', '烤箱'])
</script><style lang="scss" scoped>.container {height: 100vh;background-color: #f2f2f2;}
</style>
相关文章:
【组件封装】uniapp vue3 封装一个完整的Tabs(标签页)组件教程,功能由简到杂实现讲解。
文章目录 前言一、简单版Tabs代码实现: 二、下划线带动画的TabsAPI回顾:代码实现: 三、内容区域滑动切换切换动画代码实现:(2)禁用手势滑动切换(3)内容区域换为插槽 四、标签栏可滚动…...
TDesign:Picker 选择器
Picker 选择器 API文档地址 单列选择器用法 /// view onTap:(){TDPicker.showMultiPicker(context,data: [controller.coinList],title: ,rightTextStyle: TextStyle(color: AppColors.ColorMain),onConfirm: (selected) {controller.onTapCoin(selected);Navigator.of(contex…...
【AI赋能心理学论文创作策略】第十二章 AI辅助临床启示撰写指南
AI赋能心理学论文创作策略-系列文章目录 第十二章 AI辅助临床启示撰写指南 文章目录 AI赋能心理学论文创作策略-系列文章目录第十二章 AI辅助临床启示撰写指南 前言基础分析框架第一阶段:核心要素分析第二阶段:应用场景展开 关键环节提示第三阶段&#x…...
Pynsist 打包应用 和 PyWebIO 构建Web 应用
Pynsist:一键打包Python 应用代码为Windows 安装程序。 项目地址: https://github.com/takluyver/pynsist PyWebIO:为Python 开发者提供了一种快速、简洁的方式来创建Web 应用,无需学习前端技术 项目地址:https://g…...
git 使用配置
新拿到机器想配置git 获取代码权限,需要的配置方法 1. git 配置用户名和邮箱 git config --global user.name xxxgit config --global user.email xxemail.com 2. 生成ssh key ssh-keygen -t rsa -C "xxemail.com" 3. 获取ssh key cat ~/.ssh/id_rsa.…...
记一次Mysql的SELECT command denied to user...报错(非权限问题)
java.sql.SQLSyntaxErrorException: SELECT command denied to user ‘user_name’‘1.1.1.1’ for table ‘table_name’。错误信息的字面意思是:表“table_name”拒绝用户“user_name”“1.1.1.1”的SELECT命令 。 比较多的情况是:用户没有查看user表…...
element-plus的el-tree的双向绑定
el-tree改造了下 可选可取消 有默认值 不包含父级id 默认展开 点击节点也可触发选择 节点内容自定义 <template>{{ childKeys }}<!--default-checked-keys:默认展开值(正常来说需要包含父级id的 但是我们后端不要后端id )show-checkbox&#x…...
代码随想录-算法训练营day41(动态规划04:01背包,01背包滚动数组,分割等和子集)
第九章 动态规划part04● 01背包问题,你该了解这些! ● 01背包问题,你该了解这些! 滚动数组 ● 416. 分割等和子集 正式开始背包问题,背包问题还是挺难的,虽然大家可能看了很多背包问题模板代码…...
c#中context.SaveChanges()方法
跟踪实体的状态: Entity Framework 使用 Change Tracker 来跟踪上下文中所有实体的状态。实体的状态可以是: Added:新添加的实体(即将插入到数据库中)。Modified:已修改的实体(即将更新数据库中…...
李飞飞首个“空间智能”模型发布:一张图,生成一个3D世界 | LeetTalk Daily
“LeetTalk Daily”,每日科技前沿,由LeetTools AI精心筛选,为您带来最新鲜、最具洞察力的科技新闻。 在人工智能技术迅速发展的背景下,李飞飞创立的世界实验室于近期发布了首个“空间智能”模型,这一创新成果引发了3D生…...
Node.js简单接口实现教程
Node.js简单接口实现教程 1. 准备工作 确保您的计算机已安装: Node.js (建议版本16.x以上)npm (Node包管理器) 2. 项目初始化 # 创建项目目录 mkdir nodejs-api-tutorial cd nodejs-api-tutorial# 初始化npm项目 npm init -y# 安装必要依赖 npm install expres…...
AIGC 012-Video LDM-更进一步,SD作者将LDM扩展到视频生成任务!
AIGC 012-Video LDM-Stable Video diffusion前身,将LDM扩展到视频生成任务! 文章目录 0 论文工作1论文方法实验结果 0 论文工作 Video LDM作者也是Stable diffusion的作者,作者在SD的架构上进行扩展,实现了视频的生成。后续在Vid…...
windows文件下换行, linux上不换行 解决CR换行符替换为LF notepad++
html文件是用回车换行的,在windows电脑上,显示正常。 文件上传到linux服务器后,文件不换行了。只有一行。而且相关js插件也没法正常运行。 用notepad查看,显示尾部换行符,是CR,这就是原因。CR是不被识别的。…...
npm, yarn, pnpm之间的区别
前言 在现代化的开发中,一个人可能同时开发多个项目,安装的项目越来越多,所随之安装的依赖包也越来越臃肿,而且有时候所安装的速度也很慢,甚至会安装失败。 因此我们就需要去了解一下,我们的包管理器&#…...
静态链接和动态链接的特点
静态链接 链接方式:在编译时,所有依赖的库代码被直接打包到生成的可执行文件中。这意味着在程序运行时,不需要再加载任何外部库文件。 优点: 独立性强:生成的可执行文件可以在没有依赖库的系统上直接运行&am…...
Mac曲线救国实现Bandizip右键一级菜单
一、前言 个人认为:Bandizip是Mac上最好用的压缩软件,没有之一。 在Mac系统上,学习版的Bandizip由于签名检验问题无法在访达右键的一级菜单显示 解压相关菜单。 有能力的,希望还是支持正版,找找优惠渠道应该100左右。…...
进度与预算
一个项目,如果进度上可以按时完成,一般来说预算不会超标,或者超标幅度有限。 一个项目,如果进度上严重超期,预算基本上会超标,而且超标很大。 现在很多项目,人力成本占比都比较大,…...
【教程】创建NVIDIA Docker共享使用主机的GPU
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 这套是我跑完整理的。直接上干货,复制粘贴即可! # 先安装toolkit sudo apt-get update sudo apt-get install -y ca-certifica…...
CEEMDAN-CPO-VMD二次分解(CEEMDAN+冠豪猪优化算法CPO优化VMD)
CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD) 目录 CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 首先运用CEEMDAN对数据进行一次分解ÿ…...
图论理论基础和存储方式的实现
图论1 图论 (Graph theory) 是数学的一个分支,图是图论的主要研究对象。图 (Graph) 是由若干给定的顶点及连接两顶点的边所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
