前言
废话不多说,我们来直接看到要实现的效果: (没错,我又来写写前端,水博文了,嘿嘿·) 实现效果:
- 在视角内的卡片看起来有规律的律动
- 图片光标放上去,有放大效果
结构
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这条路,钱给的不够啊~