毕设之-Hlang社区-前端律动卡片

前言

废话不多说,我们来直接看到要实现的效果: (没错,我又来写写前端,水博文了,嘿嘿·) 实现效果:

  1. 在视角内的卡片看起来有规律的律动
  2. 图片光标放上去,有放大效果

结构

okey,由于这个效果比较见简单,所有,就非常happy,

html 复制代码
        <div class="center">
            <!-- 后面这里有个循环,负责动态加载数据 -->
            <div class="card-item" 
                v-for="(item, index) in items" :key="index" 
                :id=item.styleId
                @mouseover="handleMouseOver(item)"
                @mouseout="handleMouseOut(item)"
                @click="goCloumnBlogs"
                >
                <div class="img-box">
                    <img :src=item.imgSrc class="card-img">
                </div>
                <div class="card-title">
                    <u-fold line="1">
                        <h3>{{ item.title }}</h3>
                    </u-fold>
                </div>
                <div class="card-context">
                    <u-fold line="3">
                        {{ item.context }}
                    </u-fold>
                </div>
            </div>
        </div>

我们就这几个东西,包裹起来,因为哪些律动效果,都是用Jscript实现的,没办法,前端比较糟糕。

实现

图片放大

首先是图片的一个放大效果,这个效果主要就是,鼠标放上去,然后这个图片放缩的效果。那么这里的实现比较简单,要注意的一个点就是,我们的那个img,我们要再来一个div保住这个东西。放大了over一下就好了。

css 复制代码
.card-img {
    width: 100%;
    height: 150px;
    padding: 2px;
}

.img-box {

    width: 100%;
    height: 150px;
    overflow: hidden;
}

.card-img:hover {
    animation: move .5s linear forwards;
}

@keyframes move {
    0% {
        transform: scale(1);
    }

    100% {
        transform: scale(1.1);
    }
}

然后绑定个动画就可以了。

律动

然后就是律动效果:

首先的话,我们还是来看到这个css,因为我这里其实还是css配合js来实现的。没办法

css 复制代码
#up {
    
    /* 盒子阴影 */
    box-shadow: 4px 20px 40px 5px rgba(6, 100, 224, 0.1);
    /* 盒子向上移动效果,改变上外边距 */
    margin-top: 26px;
    transform: scale(1.1);

}

#down{
    animation: move .2s ease-in reverse;
}

@keyframes move {
    0% {
        transform: scale(1);
    }

    100% {
        transform: scale(1.1);
    }
}


.card-item {
    
    cursor: pointer;
    width: 150px;
    margin-top: 30px;
    margin-bottom: 30px;
    display: flex;
    flex-direction: column;
    background: whitesmoke;
    height: 250px;
    justify-content: center;
    align-items: center;
    border-radius: 10px;
    transition: all 0.5s ease-in;
}


.card-item:hover {
    /* 盒子阴影 */
    box-shadow: 4px 20px 40px 5px rgba(4, 59, 137, 0.5);
    /* 盒子向上移动效果,改变上外边距 */
    margin-top: 26px;
    transform: scale(1.1);
}

.center {

    width: 75%;
    align-content: flex-start;
    margin: 20px auto;
    display: flex;
    column-gap: 40px;
    flex-flow: wrap;
}

这几个css定义了它是怎么样的一个效果,然后实现律动的一个效果的话,就是去修改对应的一个box的一个属性,然后就ok了。

js 复制代码
// 得到指定的在可视区域内的元素
async function traverseVisibleElements(classSelect:string,doing:Function) {


  if(going.value!=false){
    return
  }
  going.value = true;
  const elements = document.querySelectorAll(classSelect);

  for (let i = 0; i < elements.length; i++) {
    const element = elements[i];
    const rect = element.getBoundingClientRect();

    if (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    ) {
    //   console.log('元素在屏幕可视区域内:', element);
     await doing(element,i)

    }
  }

  going.value = false;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

//按照周期修改元素属性,实现元素周期放大的效果
async function flowUp(element:Element,index){
    
    let sleepTime = Math.abs(Math.sin(((index*Math.PI)/10))*1000)
    element.id = "down"
    await sleep(sleepTime)
    element.id = "up"
    console.log(sleepTime)
}

// 计算每个 item 的初始位置和周期函数值
onMounted(() => {
  traverseVisibleElements(".card-item",flowUp)
  setInterval(() => {
    traverseVisibleElements(".card-item",flowUp)
  },1500); // 刷新间隔
}
);

// 鼠标悬停时取消特效
const handleMouseOver = (item:ItemData) => {
  item.styleId='down'
};

const handleMouseOut = (item:ItemData) => {
    item.styleId='up'
};

这里的话,扫描到在范围内的一个符合条件的盒子,然后定时去刷新,改动属性,然后每个元素按照一个周期函数的间隔去动态修改。这个的话实现一个跳动效果。

页面完整代码

okey,这里再看到页面的一个比较完整的代码,这里的话,还没有功能代码,后面后端搭起来再说。

html 复制代码
<template>

    <aside>
        <!-- 两个按钮 -->
        <el-button class="Official" type="primary" circle>精选</el-button>
        <el-button class="Private" type="primary" circle>ALL</el-button>
    </aside>

    <body>
        <div class="center">
            <!-- 后面这里有个循环,负责动态加载数据 -->
            <div class="card-item" 
                v-for="(item, index) in items" :key="index" 
                :id=item.styleId
                @mouseover="handleMouseOver(item)"
                @mouseout="handleMouseOut(item)"
                @click="goCloumnBlogs"
                >
                <div class="img-box">
                    <img :src=item.imgSrc class="card-img">
                </div>
                <div class="card-title">
                    <u-fold line="1">
                        <h3>{{ item.title }}</h3>
                    </u-fold>
                </div>
                <div class="card-context">
                    <u-fold line="3">
                        {{ item.context }}
                    </u-fold>
                </div>
            </div>
        </div>
    </body>
</template>

<script lang="ts" setup>
import { tr } from 'element-plus/es/locale';
import { ref, computed, onMounted } from 'vue';
import {useRouter} from 'vue-router'

interface ItemData {
    imgSrc: string;
    title: string;
    context: string;
    styleId: string;
}
const items = ref<ItemData[]>([
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card1.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card2.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
    { imgSrc: "./../static/image/card3.png", title: "Java", context: "Java是一种通用的,基于类的,面向对象的编程语言。", styleId: "up" },
]);

//当前动画是否执行完毕
const going = ref(false);
const router = useRouter()

const goCloumnBlogs =()=>{
  router.push(
        {
            path:'/cloumnBlogs'
        }
    );
}


// 得到指定的在可视区域内的元素
async function traverseVisibleElements(classSelect:string,doing:Function) {


  if(going.value!=false){
    return
  }
  going.value = true;
  const elements = document.querySelectorAll(classSelect);

  for (let i = 0; i < elements.length; i++) {
    const element = elements[i];
    const rect = element.getBoundingClientRect();

    if (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    ) {
    //   console.log('元素在屏幕可视区域内:', element);
     await doing(element,i)

    }
  }

  going.value = false;
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

//按照周期修改元素属性,实现元素周期放大的效果
async function flowUp(element:Element,index){
    
    let sleepTime = Math.abs(Math.sin(((index*Math.PI)/10))*1000)
    element.id = "down"
    await sleep(sleepTime)
    element.id = "up"
    console.log(sleepTime)
}

// 计算每个 item 的初始位置和周期函数值
onMounted(() => {
  traverseVisibleElements(".card-item",flowUp)
  setInterval(() => {
    traverseVisibleElements(".card-item",flowUp)
  },1500); // 刷新间隔
}
);

// 鼠标悬停时取消特效
const handleMouseOver = (item:ItemData) => {
  item.styleId='down'
};

const handleMouseOut = (item:ItemData) => {
    item.styleId='up'
};


</script>

<style scoped>

.Official{
  position: fixed;
  left: 30px;
  top: 200px;
  height: 80px;
  width: 80px;
}
.Private{
  position: fixed;
  height: 80px;
  width: 80px;
  left: 20px;
  top: 300px;
}

.card-context {
    width: 100%;
    height: 100px;
}


.card-title {
    height: 50px;
    width: 100%;
}

.card-img {
    width: 100%;
    height: 150px;
    padding: 2px;
}

.img-box {

    width: 100%;
    height: 150px;
    overflow: hidden;
}

.card-img:hover {
    animation: move .5s linear forwards;
}

#up {
    
    /* 盒子阴影 */
    box-shadow: 4px 20px 40px 5px rgba(6, 100, 224, 0.1);
    /* 盒子向上移动效果,改变上外边距 */
    margin-top: 26px;
    transform: scale(1.1);

}

#down{
    animation: move .2s ease-in reverse;
}

@keyframes move {
    0% {
        transform: scale(1);
    }

    100% {
        transform: scale(1.1);
    }
}


.card-item {
    
    cursor: pointer;
    width: 150px;
    margin-top: 30px;
    margin-bottom: 30px;
    display: flex;
    flex-direction: column;
    background: whitesmoke;
    height: 250px;
    justify-content: center;
    align-items: center;
    border-radius: 10px;
    transition: all 0.5s ease-in;
}


.card-item:hover {
    /* 盒子阴影 */
    box-shadow: 4px 20px 40px 5px rgba(4, 59, 137, 0.5);
    /* 盒子向上移动效果,改变上外边距 */
    margin-top: 26px;
    transform: scale(1.1);
}

.center {

    width: 75%;
    align-content: flex-start;
    margin: 20px auto;
    display: flex;
    column-gap: 40px;
    flex-flow: wrap;
}

body {

    width: 96%;
    margin: 0 auto;
}
</style>

总结

okey,水完了一篇,嘿嘿~ 后面我们再挑战一下几天写个推荐框架去玩玩~。后面俺们再转golang去。Java这条路,钱给的不够啊~

相关推荐
恋猫de小郭14 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊9 小时前
jwt介绍
前端