Fetch the repository succeeded.
<!--
* @Author: 李双江
* @Date: 2025/5/3 20:58:45
* @LastEditors: 李双江
* @LastEditTime: 2025/5/3 20:58:45
* @Description:
-->
<template>
<div class="card-wrapper">
<div class="card"
@mouseenter="handleMouseEnter"
@mousemove="handleMouseMove"
@mouseleave="handleMouseLeave"
@touchstart="handleMouseEnter"
@touchmove="handleMouseMove"
@touchend="handleMouseLeave"
ref="cardRef"
:style="{transform: `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`,
transition: shouldTransition?'transform 0.2s ease-out':'',
'--img-url':`url(${props.imgUrl})`,
'--foil-img-url':`url(${props.foilUrl})`,
'--pointer-x': pointerX + 'px',
'--pointer-y': pointerY + 'px',
'--radial-size':isHover?'70%':'0',
}">
<div class="shine" :style="{opacity: isHover ? 1 : 0}"></div>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, PropType, ref} from "vue";
const props = defineProps({
imgUrl: {
type: String as PropType<string>,
},
foilUrl: {
type: String as PropType<string>,
}
})
const cardRef = ref()
let cardLeft = 0
let cardTop = 0
const rotateX = ref(0)
const rotateY = ref(0)
const pointerX = ref(0)
const pointerY = ref(0)
const isHover = ref(false)
onMounted(() => {
const bcr = cardRef.value.getBoundingClientRect()
cardLeft = bcr.left
cardTop = bcr.top
})
const shouldTransition = ref(false)
const handleMouseEnter = (e:TouchEvent|MouseEvent) => {
e.preventDefault()
shouldTransition.value = true
isHover.value = true
}
const handleMouseMove = (e: any) => {
let event = e
if(e.targetTouches){
event = e.targetTouches[0]
}
if (shouldTransition.value) {
setTimeout(() => {
shouldTransition.value = false
}, 300)
}
pointerX.value = event.pageX - cardLeft
pointerY.value = event.pageY - cardTop
const posX = (event.pageX - cardLeft) - cardRef.value.offsetWidth / 2;
const posY = (event.pageY - cardTop) - cardRef.value.offsetHeight / 2
rotateX.value = posY / 10
rotateY.value = -posX / 10
}
const handleMouseLeave = () => {
shouldTransition.value = true
rotateX.value = 0
rotateY.value = 0
setTimeout(() => {
shouldTransition.value = false
}, 200)
isHover.value = false
}
</script>
<style scoped lang="scss">
.card-wrapper {
perspective: 750px;
}
.card {
--pointer-x: 0;
--pointer-y: 0;
--img-url:'';
--foil-img-url:'';
width: 367px;
height: 512px;
background-image: var(--img-url);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transform-origin: center center;
border-radius: 15px;
will-change: trasform;
overflow: hidden;
.shine {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background: radial-gradient(circle at var(--pointer-x) var(--pointer-y), rgba(255,255,255,0.6) 0, transparent 70%,transparent 100%);
transition: opacity 0.2s ease-out;
will-change: background;
}
&:after {
content: '';
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
background-image: var(--foil-img-url);
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 10px;
mask: radial-gradient(circle at var(--pointer-x) var(--pointer-y), #fff 0, transparent,var(--radial-size),transparent 100%);
filter: brightness(0.55) contrast(1.5) saturate(1);
mix-blend-mode: color-dodge;
will-change: mask, background, filter;
}
}
</style>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。