【组件封装】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) 是由若干给定的顶点及连接两顶点的边所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系。顶点用于代表事物,连接两顶点的边则用于表示两个事物…...

【实分析】【二】2.2 (c)自然数的序
文章目录 前言一、自然数的序的定义二、自然数的序的基本性质三、序的三歧性四、强归纳法原理总结 前言 在2.2 (b)的末尾,我们定义了自然数的正性,现在,我们来定义自然数的序,它是一种自然数的二元关系,通过加法进行定…...

STM32串口接收与发送(关于为什么接收不需要中断而发生需要以及HAL_UART_Transmit和HAL_UART_Transmit_IT的区别)
一、HAL_UART_Transmit和HAL_UART_Transmit_IT的区别 1. HAL_UART_Transmit_IT(非阻塞模式): HAL_UART_Transmit_IT 是非阻塞的传输函数,也就是说,当你调用 HAL_UART_Transmit_IT 时,它不会等到数据完全发…...

k8s 之storageclass使用nfs动态申请PV
文章目录 配置角色权限部署nfs-client-provisioner创建 NFS StorageClass创建 PVC 来动态申请 PV在 Pod 中使用 PVC验证存储是否正确挂载使用 kubectl 和 jq 筛选 PVCwaiting for a volume to be created, either by external provisioner "nfs-diy" or manually cre…...

vue移动端实现下载(截图)功能
前言 通过html2canvas实现截图功能然后保存 简介 html2canvas库允许我们直接在浏览器上拍摄网页或部分网页的“截图”,即浏览器实现截图的功能。 原理 屏幕截图是基于DO的。其基本原理就是读取已经渲染好的DOM元素的结构和样式信息,然后基于这些信息…...

【Golang】Golang基础语法之面向对象:结构体和方法
面向对象——结构 Go 仅支持封装,不支持继承和多态;继承和多态要做的事情交给接口来完成,即——面向接口编程。Go 只有 struct,没有 class。 定义一个最简单的树节点(treeNode)结构,方法如下&…...

【西门子PLC.博途】——在S71200里写时间设置和读取功能块
之前我们在这篇文章中介绍过如何读取PLC的系统时间。我们来看看在西门子1200里面有什么区别。同时也欢迎关注gzh。 我们在S71200的帮助文档中搜索时间后找到这个数据类型 在博途中他是一个结构体,具体为 然后我们再看看它带的读取和写入时间块 读取时间࿱…...

位运算(一)位运算简单总结
191. 位1的个数 给定一个正整数 n,编写一个函数,获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数(也被称为 汉明重量)。 示例 1: 输入:n 11 输出:3 解释:输入的二…...

工厂方法模式的理解和实践
在软件开发中,设计模式是一种经过验证的解决特定问题的通用方案。工厂方法模式(Factory Method Pattern)是创建型设计模式之一,它提供了一种创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推…...

C# 设计模式--观察者模式 (Observer Pattern)
定义 观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。观察者模式的核心在于解耦主题(被观察者)和观察者之间的依赖关系。 …...

【开发语言】层次状态机(HSM)介绍
层次状态机(Hierarchical State Machine, HSM),从基本原理、结构设计、实现方法以及如何结合 Qt 进行具体实现等方面进行分析。 1. 层次状态机的基本原理 层次状态机是一种用于管理复杂系统行为的状态机模型,它通过将状态组织成…...