整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Vuejs高度的改变动画探索:折叠面板Collapse组件的最佳实现方案

示:文章尾部有全部源码,大家可以CTRL + C直接拿走,不用谢我;当然,我还是建议大家都能通过学习自己手写实现。

使用过CSS transition属性的童鞋们应该都清楚,当元素在过渡开始或者结束时的高度为auto时,动画是不生效的;那么如何才能实现元素高度的改变动画效果呢?本篇文章将为大家提供一个基于Vue3的非常简洁的解决方案。

要实现高度的改变动画,我们需要在动画进行之前为元素设置准确的高度。

当元素由可见变为隐藏状态时,我们需要先获取元素的计算高度,将其设置到style属性上,然后执行一个触发浏览器渲染引擎重绘的动作,然后再将高度设置为0,这样高度的改变动画就会正常进行。

当元素由隐藏变为可见状态时,我们需要先将高度设置为auto,然后获取元素的计算高度,再将高度设置为0,然后执行一个触发浏览器渲染引擎重绘的动作,然后再将高度设置为计算高度,这样高度的改变动画就会正常进行。

现在,根据以上实现原理分析,我们创建一个高度的改变动画通用组件CollapseTransition.vue。该组件非常简单,仅需30多行代码。我几乎每行代码都有注释,大家应该能看懂吧?

<template>
  <transition v-bind="listeners">
    <!-- 当visible的值发生改变时,过渡组件的监听器就会触发 -->
    <div v-show="visible" class="x-collapse-transition">
      <slot />
    </div>
  </transition>
</template>
<script setup>
defineProps({ visible: Boolean })
const listeners = {
  // 元素由隐藏变为可见
  onEnter (/** @type {HTMLElement} */ el) {
    el.style.height = 'auto' // 将高度设为auto,是为了获取该元素的计算高度
    const endHeight = window.getComputedStyle(el).height // 计算高度
    el.style.height = '0px' // 将高度再设置为0
    el.offsetHeight // 强制重绘,重绘后再改变高度才会产生动画
    el.style.height = endHeight // 设置为计算高度
  },
  onAfterEnter (/** @type {HTMLElement} */ el) {
    el.style.height = null // 过渡进入之后,将高度恢复为null
  },
  // 元素由可见变为隐藏
  onLeave (/** @type {HTMLElement} */ el) {
    el.style.height = window.getComputedStyle(el).height // 计算高度
    el.offsetHeight // 强制重绘,重绘后再改变高度才会产生动画
    el.style.height = '0px' // 将高度设置为0
  },
  onAfterLeave (/** @type {HTMLElement} */ el) {
    el.style.height = null // 过渡离开之后,将高度恢复为null
  }
}
</script>
<style lang="scss">
  .x-collapse-transition {
    overflow: hidden;
    transition: height .22s ease-in-out;
  }
</style>

以上就是实现高度的改变动画的通用组件源码,童鞋们理解了吗?是不是非常简单?现在,我们实现折叠面板组件。使用过element-ui这样的UI库的童鞋们应该都知道,折叠面板是由两个组件折叠面板Collapse和折叠面板项CollapseItem组合而成;

现在,我们先实现CollapseItem.vue组件。为了节省篇幅,我将源码中的空行全部去掉了,缩进比较规范,自认为可读性还行;源码如下,一共30多行,我直接在源码中添加了注释,就不过多解释了。

<template>
  <div :class="[cls, { 'is-active': isActive }]">
    <div :class="`${cls}_header`" @click="onToggle">
      <div :class="`${cls}_title`">
        <slot name="title">{{ title }}</slot>
      </div>
      <i :class="['x-icon-arrow-right', `${cls}_arrow`, { 'is-active': isActive }]"></i>
    </div>
    <x-collapse-transition :visible="isActive">
      <div :class="`${cls}_content`">
        <slot />
      </div>
    </x-collapse-transition>
  </div>
</template>
<script setup>
import { computed, inject } from 'vue'
import { COLLAPSE_INJECT_KEY } from '../../constants' // 当它是个字符串常量就行
import { N, S, B } from '../../types' // N: Number, S: String, B: Boolean
import { genKey } from '../../utils' // 生成唯一性的数值key,之前的文章中有源码
import XCollapseTransition from './CollapseTransition.vue' // 折叠动画组件
const props = defineProps({
  name: [S, N],
  title: S,
  disabled: B
})
const collapse = inject(COLLAPSE_INJECT_KEY, '') // 注入折叠面板组件提供的一些属性和方法
const cls = 'x-collapse-item'
const idKey = computed(() => props.name || genKey()) // 如果没提供name,我们生成一个key
const isActive = computed(() => collapse.includes(idKey.value)) // 是否展开状态
function onToggle () { // 内容可见时隐藏,隐藏时可见
  collapse.updateModel(idKey.value)
}
</script>

这是CollapseItem.vue组件的样式。

@import './common/var.scss';
.x-collapse-item {
  font-size: 13px;
  background-color: #fff;
  color: $--color-text-primary;
  border-bottom: 1px solid $--border-color-lighter;
  &:first-child {
    border-top: 1px solid $--border-color-lighter;
  }
  &_header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 48px;
    cursor: pointer;
  }
  &_content {
    line-height: 1.8;
    padding-bottom: 25px;
  }
  &_arrow {
    margin-right: 8px;
    transition: transform .2s;
    &.is-active {
      transform: rotate(90deg);
    }
  }
}

现在,我们实现Collapse.vue组件。该组件仍然只有30多行,大家理解起来应该很轻松,我就直接在源码里添加注释作为讲解了;

<template>
  <div class="x-collapse">
    <slot />
  </div>
</template>
<script setup>
import { provide, reactive, ref, watch } from 'vue'
import { COLLAPSE_INJECT_KEY } from '../../constants' // 一个字符串常量
import { S, B, A } from '../../types' // 参看CollapseItem组件
const props = defineProps({
  modelValue: [S, A], // Vue3使用modelValue取代了Vue2中的value
  accordion: B // 是否手风琴模式,手风琴模式只能有1个面板项是展开状态
})
const emit = defineEmits(['update:modelValue', 'change'])
function emitValue (v) {
  emit('update:modelValue', v) // 与props.modelValue结合实现双向数据绑定
  emit('change', v)
}
const model = ref(props.modelValue)
watch(() => props.modelValue, v => model.value = v)
provide(COLLAPSE_INJECT_KEY, { // 提供2个方法用于注入子组件,给子组件调用
  includes (v) { // 根据面板的key,判断是否包含该面板项
    return props.accordion ? model.value === v : (model.value || []).includes(v)
  },
  updateModel (v) { // 更新面板项的内容折叠和展开状态
    const { value } = model
    if (props.accordion) {
      model.value = value === v ? null : v
      emitValue(model.value)
    } else {
      if (!value) model.value = []
      const index = model.value.indexOf(v)
      index > -1 ? model.value.splice(index, 1) : model.value.push(v)
      emitValue(model.value)
    }
  }
})
</script>

以上就是折叠面板组件的实现。包括折叠动画组件,一共仅需不到150行代码,是不是非常简单?童鞋们都理解了吗?不管有什么疑问,童鞋们都可以问我。感谢阅读!

讲解CSS过渡和动画 transition/animation:全方位深入实践指南

**引言:赋予网页动态生命力**

CSS过渡(Transition)与动画(Animation)是现代Web前端开发中不可或缺的设计元素,它们使静态网页变得生动活泼,极大地提升了用户体验。本文旨在以全面且易于理解的方式讲解CSS过渡与动画的工作原理、应用场景及其具体实现方法,辅以实例代码,帮助您更好地理解和掌握这一关键技术。

---

### **一、CSS过渡基础概念**

**标题:走进CSS Transition的世界**

**1. 过渡是什么?**

CSS过渡是一种视觉效果,它能够在CSS属性值发生变化时平滑地过渡。例如,当鼠标悬停在一个元素上时,其背景颜色可以从白色逐渐过渡到黑色。

**2. 如何定义过渡?**

过渡由三个关键部分组成:**触发器**(何时启动过渡)、**持续时间**(过渡多久完成)以及**执行方式**(如何变化)。

```css

/* 基础过渡设置 */

.example {

transition: property duration timing-function delay;

}

/* 示例 */

.box {

width: 100px;

height: 100px;

background-color: red;

transition: background-color 0.5s ease-in-out;

}

```

在此示例中,`.box`元素的背景色会在属性值改变时,在0.5秒内平滑过渡。

---

### **二、过渡触发方式**

**标题:触发动画的关键时刻**

过渡通常在CSS属性值发生改变时自动触发,常见的触发方式有:

- **伪类触发**(`:hover`, `:focus`, `:active`等)

- **JavaScript操作触发**(通过更改DOM元素样式)

```html

<!DOCTYPE html>

<html lang="en">

<head>

<style>

.box {

width: 100px;

height: 100px;

background-color: red;

transition: background-color 0.5s ease-in-out;

}

.box:hover {

background-color: blue;

}

</style>

</head>

<body>

<div class="box"></div>

</body>

</html>

```

在这个例子中,当鼠标悬停在`.box`元素上时,背景颜色会通过过渡效果变蓝。

---

### **三、CSS动画进阶详解**

**标题:构建复杂的动画效果**

CSS动画比过渡更为灵活,允许开发者定义一系列关键帧(@keyframes),并在指定时间段内逐步变换样式。

**1. 创建关键帧规则**

```css

@keyframes fadeInOut {

0% { opacity: 0; }

50% { opacity: 1; }

100% { opacity: 0; }

}

```

上述代码定义了一个名为`fadeInOut`的动画,元素将在动画期间经历透明度从0到1再到0的变化。

**2. 应用动画**

```css

.box {

animation-name: fadeInOut;

animation-duration: 2s;

animation-iteration-count: infinite;

animation-direction: alternate;

}

```

通过`animation-*`系列属性,我们将`fadeInOut`动画应用于`.box`元素,使其无限循环交替播放,每次持续2秒。

---

### **四、动画高级特性**

**标题:探索动画的更多可能性**

- **动画填充模式**(animation-fill-mode)控制动画结束后元素的样式状态。

- **动画延时**(animation-delay)决定动画何时开始。

- **动画播放次数**(animation-iteration-count)可设置动画循环次数,甚至使用`infinite`无限循环。

- **动画方向**(animation-direction)影响动画是否逆序播放。

---

### **五、综合案例分析**

**标题:实际项目中的CSS过渡与动画运用**

此处可以结合实际项目需求,给出一个或多个包含复杂过渡和动画交互的HTML+CSS示例,比如响应式导航菜单的展开收起动画、轮播图切换过渡效果等,以增强文章的实用性。

---

**结语:**

熟练掌握CSS过渡和动画,能让您的网页设计更具表现力和吸引力。无论是微小的UI细节还是复杂的交互场景,善用过渡和动画都能有效提升用户体验。记住,适时适度的动态效果才是锦上添花,过度则可能适得其反。希望通过这篇文章,您能够洞悉CSS过渡与动画的奥秘,并在实践中游刃有余地运用这些强大的工具。

天我们学习的内容有:过渡,动画,转换,伸缩盒子。

可以说今天学习的内容都是重量级的大佬,学好了,使用css3做出酷炫的效果 So Easy!~~

1.过渡

在css3中,有一个属性可以设置过渡效果。

它就是transition,所谓的过渡效果,指的就是以动画的形式慢慢演化样式属性变化的过程。

A.案例:通过transition设置焦点过渡效果

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>div{width: 200px;height: 200px;margin:200px;background: url(girl.jpg);border-radius:50%;transition:all 1s linear 0.3s;cursor: pointer;}div:hover{box-shadow: 0px 0px 20px blue;}</style></head><body><div></div></body></html>

注意页面中的代码:

第一,我们给div添加了一个hover伪类样式,当我们鼠标悬停在div上方的时候,会给div盒子添加一个蓝色的盒子阴影。

第二,我们给div盒子添加了一个transition样式,设置的值为:all 1s linear 0.3s;

这四个数据分别对应

transition-property(需要过渡的属性):如果设置为all表示所有样式属性都需要过渡。

transition-duration(过渡的时间):以秒作为单位,设置过渡的时间

transition-timing-function(过渡的方式):常用的有linear(匀速),ease(先慢后快),ease-in,ease-out,ease-in-out等

transition-delay(延迟的时间):以秒作为单位进行延迟,延迟之后开始进行过渡效果。

所以,我们通过transition这个复合属性设置的过渡效果为:

all:需要过渡所有的属性

1s:过渡的时间为1秒

linear:匀速过渡

0.3s:在延迟0.3秒之后开始过渡动画。

如果大家理解了上面的描述,那么也就不难理解咱们鼠标放到div上之后,为啥会慢慢出现蓝色的光晕了,就是因为咱们添加了过渡,所以,慢慢的就会给盒子添加阴影效果。

2.动画:

在学习完了过渡之后,发现咱们可以使用transition去以动画的形式展示样式的改变以及变化的过程,这可以帮助我们来实现一些过渡的动画。

但是,有的时候,我们的需求会更加的复杂,要求会更加的多变,那么,transition可能就无法满足我们的需要了,我们需要有更加炫酷,复杂的效果呈现。

那么,动画animation就可以满足我们的需要。

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>@keyframes moveAndChange{0%{left:0px;top:0px;}25%{left:200px;top:200px;background:green;border-radius: 0;}50%{left:400px;top:200px;background:blue;border-radius: 50%;}75%{left:400px;top:0px;background:#ccc;border-radius: 0;}100%{left:0px;top:0px;background:red;border-radius: 50%;}}div{margin:200px;width: 200px;height: 200px;position: absolute;background:red;border-radius:50%;animation: moveAndChange 5s linear 0.5s infinite normal;}</style></head><body><div></div></body></html>

代码效果如下:

同样,让我们来关注编写的代码:

1.在样式中,首先我们使用@keyframes 来定义了一个复杂的动画,在css3中,新增了@keyframes可以来帮助我们添加动画。代码如下:

/*动画的名字叫做moveAndChange*/

@keyframes moveAndChange{

/*动画最初的时候,将left设置为0px,top设置为0px*/

0%{

left:0px;

top:0px;

}

/*当动画进行到25%的时候,使用动画将left过渡到200px,top过渡到200px,

背景颜色过渡为绿色,圆角过渡为0(无圆角)*/

25%{

left:200px;

top:200px;

background:green;

border-radius: 0;

}

/*当动画进行到50%的时候,使用动画将left过渡到400px,top过渡到200px,

背景颜色过渡为蓝色,圆角过渡为50%(正圆)*/

50%{

left:400px;

top:200px;

background:blue;

border-radius: 50%;

}

/*当动画进行到75%的时候,使用动画将left过渡到400px,top过渡到0,

背景颜色过渡为灰色,圆角过渡为0(无圆角)*/

75%{

left:400px;

top:0px;

background:#ccc;

border-radius: 0;

}

/*当动画结束的时候,使用动画将left过渡到0x,top过渡到0px,

背景颜色过渡为红色,圆角过渡为50%(正圆)*/

100%{

left:0px;

top:0px;

background:red;

border-radius: 50%;

}

}

这是一个比较复杂的动画效果,可以发现,它通过百分比的形式将一个完整的动画拆分成了5个部分,每个部分都有不同的样式效果,而当我们采用该动画的元素就会按照设置的顺序和样式效果进行动画的过渡和展示。

2.上面我们只是通过@keyframes创建了一个动画,我们还需要通过特定的语法来使用这个动画。

就是下面这句代码了:

animation: moveAndChange 5s linear 0.5s infinite normal;

它是一个复合属性,设置了6个值,分别对应:

animation-name(设置动画的名称):用来设置动画的名字,我们这里写的是moveAndChange ,也就是说我们就是要使用我们刚刚创建的动画。

animation-duration(设置整个动画的时间):以秒作为单位,我们这里写的是5s,表示整个动画的时间为5秒

animation-timing-function(设置播放动画的方式):播放动画的方式,常用的有linear(匀速),ease(先慢后快),ease-in,ease-out,ease-in-out等,我们使用的是linear匀速播放动画。

animation-delay(设置动画的延迟):以秒作为单位,我们写的是0.5s,表示延迟0.5秒之后开始播放动画。

animation-iteration-count(设置动画播放的次数):播放动画的次数,我们这里写的是infinite ,表示动画将会被播放无限次,如果写数字,那么就会播放数字对应的次数。

animation-direction(设置是否反向播放动画):我们写的是normal,表示正常播放动画,如果写的是

alternate则表示要反向播放动画,大家也可以自己试一试这个效果。

最终,我们通过@keyframes创建动画,通过animation设置动画,成功完成了这个复杂的动画效果。

3.转换

在css3中,我们通过transform属性可以设置元素的转换效果,具体的效果如下:

A.平移

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>body{background:pink;}div{width: 200px;height: 200px;position: absolute;background: green;left:0px;top:0px;transform: translate(300px,300px);}</style></head><body><div></div></body></html>

代码效果如下:

如上图所示,本来div盒子的位置是left:0,top:0;

但是我们通过transform: translate(300px,300px);将盒子进行了偏移,所以,盒子的位置发生了改变。

B.旋转

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>body {background: pink;}div {width: 200px;height: 200px;margin: 200px;position: absolute;background: green;left: 0px;top: 0px;transform: rotate(45deg);}</style></head><body><div></div></body></html>

代码效果如下:

如上图所示,本来div盒子应该是四四方方的。

但是,经过我们的代码transform: rotate(45deg); //deg为单位,表示度数。

进行了45度的旋转之后,呈现出来的就是一个菱形的盒子了,旋转的正方向为顺时针,负方向为逆时针。

C.缩放

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>body {background: pink;}div {width: 200px;height: 200px;margin: 200px;position: absolute;background: green;left: 0px;top: 0px;transform: scale(0.5,0.25);}</style></head><body><div></div></body></html>

代码效果如下:

如上图所示,本来盒子的宽高为200*200,而我们通过transform: scale(0.5,0.25);进行的缩放

scale的第一个参数为0.5,表示横向缩小为0.5倍

scale的第二个参数为0.25,表示纵向缩小为0.25倍。

scale的参数如果为1,则表示不进行任何缩放,小于1就是做缩小,而大于1表示做放大。

小结:transform转换中其实还包含了skew(倾斜),matrix(矩阵转换),相对来说用到的不是特别多,所以在本文中我们便不再做介绍。

4.flex布局

Flex布局,可以简便、完整、响应式地实现各种页面布局。

Flex是Flexible Box的缩写,翻译成中文就是“弹性盒子”,用来为盒装模型提供最大的灵活性。任何一个容器都可以指定为Flex布局。

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>div{box-sizing: border-box;}.parent {width: 600px;height: 200px;margin: 100px;position: absolute;background: green;left: 0px;top: 0px;display: flex;justify-content: flex-start}.parent div{width: 20%;border:1px solid #ccc;background:pink;}</style></head><body><div><div>1</div><div>2</div><div>3</div><div>4</div></div></body></html>

代码效果如下:

如图所示,咱们通过display:flex将.parent元素设置为了flex盒子,那么子元素将会按照justify-content设置的方式进行元素的排列,目前看来,和我们没有设置flex盒子的效果是一致的。

接下来我们更改一下,将justify-content设置为flex-end,效果如下图所示:

所以我们就应该发现,flex-start是让所有的子元素从父元素的左侧开始排列

而flex-end是让所有的子元素从元素的右侧开始排列。

我们再来更改一下,将justify-content设置为center,效果如下图所示:

更厉害了,子元素在父盒子的中央位置排列显示了。

然后,我们再将justify-content设置为space-around,效果如下图所示:

它是平均分配的形式为每一个子元素设置了间距,但是看起来有点变扭。

所以我们推荐将justify-content设置为space-between,效果如下图:

我们还可以通过flex-wrap来设置子元素是否换行显示,以及flex-direction设置子元素排列的顺序。

这两个属性可以设置的值如下:

flex-wrap: nowrap;//不换行,会自动收缩

flex-wrap: warp;//换行,会自动收缩

flex-wrap: warp-reverse;//反转,从默认的从上到下排列反转为从下到上。

flex-direction:row; //从左至右一行一行进行子元素的排列

flex-direction:column; //从上到下一列一列进行子元素的排列

flex-direction:row-reverse; //从右至左一行一行进行子元素的排列

flex-direction:column-reverse; //从下到上一列一列进行子元素的排列

案例代码如下:

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>div{box-sizing: border-box;}.parent {width: 600px;height: 200px;margin: 100px;position: absolute;background: green;left: 0px;top: 0px;display: flex;justify-content: space-between;flex-wrap: nowrap;flex-direction: row-reverse;}.parent div{width: 20%;border:1px solid #ccc;background:pink;}</style></head><body><div><div>1</div><div>2</div><div>3</div><div>4</div><div>5</div><div>6</div></div></body></html>

我们设置了flex-wrap: nowrap;(不换行,压缩所有的子元素在一行中显示),以及flex-direction: row-reverse;(反向排列)

代码效果如下:

如果设置为flex-wrap: warp(换行显示无法在一行显示的子元素),则效果如下:

如果将flex-direction: column;,则会纵向排列元素,效果如下图:

除了上面的这些给伸缩盒子父元素设置的样式之外,我们还可以可以伸缩盒子的子元素设置flex属性,用来设置平均分配整个父盒子的空间。

代码如下:

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>div{box-sizing: border-box;}.parent {width: 600px;height: 200px;margin: 100px;position: absolute;background: green;left: 0px;top: 0px;display: flex;justify-content: space-between;}.parent div{flex:1;width: 20%;border:1px solid #ccc;background:pink;}</style></head><body><div><div>1</div><div>2</div><div>3</div><div>4</div></div></body></html>

效果如下:

如上图所示,每个盒子平均分配了父盒子的空间,原本宽度为20%,现在被拉伸了。

除此之外,咱们还可以使用flex属性进行进一步的设置,代码如下:

<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Document</title><style>div{box-sizing: border-box;}.parent {width: 600px;height: 200px;margin: 100px;position: absolute;background: green;left: 0px;top: 0px;display: flex;justify-content: space-between;}.parent div:nth-of-type(1){flex:1;border:1px solid #ccc;background:red;}.parent div:nth-of-type(2){flex:2;border:1px solid #ccc;background:green;}.parent div:nth-of-type(3){flex:2;border:1px solid #ccc;background:blue;}.parent div:nth-of-type(4){flex:1;border:1px solid #ccc;background:pink;}</style></head><body><div><div>1</div><div>2</div><div>3</div><div>4</div></div></body></html>

效果如下图:

我们分别给四个子盒子设置了flex:1 , flex:2, flex:2 ,flex:1.

这是什么意思呢?

四个flex加起来一共是6.那么第一个盒子就占据整个父盒子的1/6宽度。

同理,另外三个盒子分别占据2/6,2/6,1/6的宽度,所以就形成了我们现在看到的效果。

原文来源于:黑马程序员社区


学习资源:

想学习css,可以关注:黑马程序员头条号,后台回复:css