nuxt实现50个前端小创意(1)——前端基础学习

首先感谢@Chengyunlai 的开源✔️

本文所有内容基建于开源项目✔️

欢迎大家去看项目的README,更加精简直接,配套学习更佳✔️

欢迎大家STAR!如果你也热爱前端基础的学习,一起来吧✔️

[项目介绍]

第一个项目是可伸缩类手风琴图形方块。效果如下图

\首先我们对于动效进行分解:

  • 点击实现目标图片横向展开放大:一个点击事件

  • 已放大图片收缩:消除上次点击图片的放大效果,转换为默认状态

  • 图片的收缩和扩大以动画实现平滑过渡

/源JS代码

js 复制代码
const panels = document.querySelectorAll('.panel')

panels.forEach(panel => {
    panel.addEventListener('click', () => {
        removeActiveClasses()
        panel.classList.add('active')
    })
})

function removeActiveClasses() {
    panels.forEach(panel => {
        panel.classList.remove('active')
    })
}

\简单解读:

  • 选中所有 .panel 元素(方便统一操作)

panels 是一个 NodeList,其数据结构类似于数组,这样我们就可以用.forEach给每个元素进行操作)

  • 为每个 .panel 添加点击事件.

  • 每次点击时,先清除所有 .panelactive 类,再给当前点击的那个添加 active 类. (active类来自于CSS中.)

  • 这样就能达到"点击一个卡片,它展开,其他都收缩"的效果.

/源CSS代码如下

css 复制代码
@import url('https://fonts.googleapis.com/css?family=Muli&display=swap');  
/* 从 Google Fonts 引入 "Muli" 字体,display=swap 表示字体加载失败时先显示系统字体 */

* {
  box-sizing: border-box;
  /* 所有元素使用 border-box 模型,可以控制元素宽高 */
}

body {
  font-family: 'Muli', sans-serif;
  /* 设置页面字体为 Muli,若加载失败使用 sans-serif */

  display: flex;
  /* 使用 Flexbox 布局 */

  align-items: center;
  /* 垂直方向居中对齐子元素 */

  justify-content: center;
  /* 水平方向居中对齐子元素 */

  height: 100vh;
  /* 设置 body 高度与浏览器窗口高度一致 */

  overflow: hidden;
  /* 当页面的内容超出了"body"元素的范围时,超出的部分会被隐藏 */

  margin: 0;
  /* 去掉默认的外边距 */
}

.container {
  display: flex;
  /* container 也使用 Flexbox,子元素 .panel 水平排列 */

  width: 90vw;
  /* 宽度为浏览器窗口宽度的 90%,响应式布局 */
}

.panel {
  background-size: cover;
  /* 背景图像等比缩放铺满容器 */

  background-position: center;
  /* 背景图像居中对齐 */

  background-repeat: no-repeat;
  /* 背景图像不重复 */

  height: 80vh;
  /* 高度为窗口高度的 80% */

  border-radius: 50px;
  /* 圆角半径为 50px,使边角圆润 */

  color: #fff;
  /* 文字颜色为白色 */

  cursor: pointer;
  /* 鼠标悬停时显示指针,表示可点击 */

  flex: 0.5;
  /* 初始占据容器宽度的 0.5 单位 */

  margin: 10px;
  /* 元素之间间距为 10px */

  position: relative;
  /* 相对定位,便于内部绝对定位子元素(如 h3) */

  -webkit-transition: all 700ms ease-in;
  /* 平滑过渡动画,持续 700ms,开始时较慢 */
}

.panel h3 {
  font-size: 24px;
  /* 设置标题字体大小为 24px */

  position: absolute;
  /* 绝对定位,相对于 .panel */

  bottom: 20px;
  /* 离底部 20px */

  left: 20px;
  /* 离左边 20px */

  margin: 0;
  /* 去除默认外边距 */

  opacity: 0;
  /* 默认透明,不显示标题 */
}

.panel.active {
  flex: 5;
  /* 激活状态时,占据 5 倍空间,实现放大效果 */
}

.panel.active h3 {
  opacity: 1;
  /* 激活时标题变为可见 */

  transition: opacity 0.3s ease-in 0.4s;
  /* 标题透明度动画:0.4 秒延迟后开始,0.3 秒缓慢进入 */
}

@media (max-width: 480px) {
  /*进行移动端优化, 当屏幕宽度小于等于 480px(如手机屏幕)时 */

  .container {
    width: 100vw;
    /* container 宽度占满整个视口宽度 */
  }

  .panel:nth-of-type(4),
  .panel:nth-of-type(5) {
    display: none;
    /* 第 4 和第 5 个 panel 在小屏设备上隐藏,提高可视性和响应性能 */
  }
}

为了实现放大效果,利用了flex,其中.panel.active{flex:5;},等价于

js 复制代码
.panel.active {

  flex-grow: 5;    /* 扩展比例 */
  
  flex-shrink: 1;  /* 收缩比例(默认值)*/
  
  flex-basis: 0%;  /* 初始尺寸(默认值)*/
  
}

要点总结:flex控制元素大小变化,结合transition动画,实现平滑过渡。

flex能够弹性设置一维布局的动态比例,有更好的视觉效果,例如在本次项目中:

  • 总共 5 个面板

  • 其中一个是 active(flex: 5),其他四个是普通的(flex: 0.5)

  • 那总 flex 值是:5 + 0.5 * 4 = 7

  • 所以占比:

    • active:5 / 7 ≈ 71%
    • 其他普通面板:0.5 / 7 ≈ 7.1%

✔️ 这就自然分配好了空间,且始终不会超出容器宽度。

[VUE实现]

直接参考项目的vue代码,git完代码,yarn安装完依赖后,可以在pages对应项目文件夹中看到并运行。

/将图片card分离模块化到data.ts文件

ts 复制代码
interface Card {
    name: string;
    url: string;
}

const cards: Card[] = [
    {
        name: 'Explore The World',
        url: 'https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80'
    },
    {
        name: 'Wild Forest',
        url: 'https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80'
    },
    {
        name: 'Sunny Beach',
        url: 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1353&q=80'
    },
    {
        name: 'City on Winter',
        url: 'https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1351&q=80'
    },
    {
        name: 'Mountains - Clouds',
        url: 'https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80'
    }
];

export default cards;

/vue实现代码

js 复制代码
<script setup lang="ts">
/* 'lang'声明TypeScript */
import cards from "~/pages/expanding-cards/data";
//导入图片数据

const activeIndex = ref<number | null>(0)
//定义activeIndex,null(0)表示开始触发效果的盒子
onMounted(() => {
  // 组件加载完后,此时可以安全访问 document
  document.title = '拓展盒子'
})
</script>
js 复制代码
<template>
 
//创建一个横向排列、间距均匀的容器
// mx-10:  左右水平方向的外边距; `flex`: 启用 Flex 布局;`w-full`: 设置宽度为容器的 100%;`gap-5`: 设置子元素之间的间距 
  <div class="mx-10 flex w-full gap-5">
  
js 复制代码
    <div
        v-for="(card, index) in cards"//从data里提出cards进行for循环遍历
        :key="index"//标记每个唯一的盒子
        :style="{ backgroundImage: 'url(' + card.url + ')' }"//为每个盒子设置图片
        class="relative h-[80vh] flex-[0.5] rounded-lg bg-cover bg-center transition-all duration-700 ease-in hover:cursor-pointer"
         //-   `h-[80vh]`: 卡片高度占浏览器视口 80%;
        //`flex-[0.5]`: 初始flex设置为0.5; 
       //`rounded-lg`: 圆角;
      //`bg-cover`, `bg-center`: 背景图填满容器并居中;
     //`transition-all duration-700 ease-in`: 动画过渡属性;
    //`hover:cursor-pointer`: 鼠标悬停时显示手型;
        :class="{ 'flex-[5]': index === activeIndex }"//如果当前盒子响应,flex设置为5,"==="严格等于保证未激活盒子保持默认flex-[0.5]
        @click="activeIndex = index"//定义点击事件,点击激活盒子响应
    >
js 复制代码
    <h3
        //文字默认设置
        class="absolute bottom-20 left-20 text-2xl font-bold text-white opacity-0"
        :class="{ 'opacity-100 delay-[400ms] transition-opacity duration-300 ease-in': index === activeIndex }"
    >//文字响应后的设置
js 复制代码
   </h3>
 </div>
</div>
</template>
Vue 功能/组件 功能描述 用途说明
<script setup> 用更少的代码写组件逻辑 让你不必在vue里写 export default,代码更短、更清晰,逻辑和模板一目了然
ref() 创建一个"可自动更新"的变量 当这个变量的值变化时,页面上的内容会自动刷新,无需手动操作
onMounted() "组件准备好"时自动运行代码 组件加载完成后做一次性操作,比如给页面贴标题或发起数据请求
<template> 写组件的 HTML 模板 把变量、指令和 HTML 结构放在一起,决定页面长什么样
v-for 按数组数量生成多个元素 自动为数组中每一项创建一个 DOM 元素
:key 给每个循环出来的元素一个标识 帮 Vue 快速找到对应的元素,避免重复渲染和奇怪的闪烁
:style 给元素设置"实时更新"的样式 根据图片地址动态填背景图,不用写死 CSS
:class 给元素添加或移掉 CSS 类 根据条件给卡片放大、文字渐显等效果
@click 为元素绑定点击动作 用户点一下就能触发函数,切换激活卡片或执行其他交互

[动画扩展:更Q弹]

对于transition动画加一个贝塞尔曲线的微调,实现先回拉后展开,像橡皮筋有个回弹效果

js 复制代码
 :style="{ backgroundImage: 'url(' + card.url + ')'  }"
 
 ==>修改为
 
 :style="{ backgroundImage: 'url(' + card.url + ')' , transitionTimingFunction: 'cubic-bezier(0.61,-0.19,0.7,-0.11)'  }"
 

效果如下图

本文可能部分存在错误,或者排版问题,欢迎各位指教,我将及时更正,大家对相关知识感兴趣也可以一起讨论,一起学习进步😃

相关推荐
无名之逆1 小时前
[特殊字符]For Speed Enthusiasts: The Ultimate Evolution of Rust HTTP Engines
开发语言·前端·后端·网络协议·http·rust
巴巴_羊1 小时前
前端八股HTTP和https大全套
前端·http·https
不写八个2 小时前
Express教程【002】:Express监听GET和POST请求
前端·javascript·express
pianmian17 小时前
3D Tiles高级样式设置与条件渲染(3)
linux·服务器·前端
资深前端之路7 小时前
vue+threeJs 绘制3D圆形
前端·javascript·vue.js
Nymph_Zhu8 小时前
vue3+element-plus el-date-picker日期、年份筛选设置本周、本月、近3年等快捷筛选
前端·vue.js·elementui
极客密码8 小时前
DeepSeek-R1-0528,官方的端午节特别献礼
前端·ai编程·deepseek
打小就很皮...9 小时前
npm、pnpm、yarn使用以及区别
前端·npm·yarn
FungLeo9 小时前
vue2 + webpack 老项目升级 node v22 + vite + vue2 实战全记录
前端·webpack·vue2·vie·webpack 升级 vite
西洼工作室9 小时前
使用原生前端技术封装一个组件
前端·js