<template>
<div
class="tilt-container"
:class="{
darkmode: theme === 'dark'
}"
:style="{
borderRadius: shadowBorderRadius + '%'
}"
ref="container"
>
<div
class="tilt-image"
:style="{
border: border ? border : (theme === 'dark' ? '10px solid white' : '10px solid black')
}"
v-bind="$attrs"
>
<slot>
<img
src="@/assets/alps.png"
alt="tilt image"
/>
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'Tilt',
inject: ['theme'],
props: {
maxTilt: {
type: Number,
default: 8,
control: 'slider',
min: 0,
max: 45
},
shadowBlur: {
type: Number,
default: 60,
control: 'slider',
min: 0,
max: 300
},
shadowDistanceX: {
type: Number,
default: 3,
control: 'slider',
min: 0,
max: 20
},
shadowDistanceY: {
type: Number,
default: 3,
control: 'slider',
min: 0,
max: 20
},
shadowBorderRadius: {
type: Number,
default: 10,
control: 'slider',
min: 0,
max: 100
},
shadowColor: {
type: String,
default: '',
control: 'color'
},
border: {
type: String,
default: ''
}
},
mounted() {
window.addEventListener('mousemove', this.handleMouseMove);
window.addEventListener('mouseleave', this.resetTilt);
},
beforeUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove);
window.removeEventListener('mouseleave', this.resetTilt);
},
computed: {
shadowColored() {
if (this.shadowColor) return this.shadowColor;
return this.theme === 'dark' ? 'rgba(255, 255, 255, 0.25)' : 'rgba(0, 0, 0, 0.25)';
}
},
methods: {
handleMouseMove(e) {
const rect = this.$refs.container.getBoundingClientRect();
// center of component
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
// distance from cursor to center
const offsetX = e.clientX - centerX;
const offsetY = e.clientY - centerY;
// normalize -1 to 1
const percentX = offsetX / (rect.width / 2);
const percentY = offsetY / (rect.height / 2);
// clamp to avoid crazy tilts far outside
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
const tiltX = clamp(-percentY, -1, 1) * this.maxTilt;
const tiltY = clamp(percentX, -1, 1) * this.maxTilt;
this.$refs.container.style.transform = `rotateX(${tiltX}deg) rotateY(${tiltY}deg) translateZ(10px)`;
this.$refs.container.style.boxShadow = `${-tiltY * this.shadowDistanceX}px ${tiltX * this.shadowDistanceY}px ${this.shadowBlur}px ${this.shadowColored}`;
},
resetTilt() {
this.$refs.container.style.transform = `rotateX(0deg) rotateY(0deg) translateZ(0)`;
this.$refs.container.style.boxShadow = `0 12px ${this.shadowBlur}px ${this.shadowColored}`;
}
}
};
</script>
<style scoped>
.tilt-container {
display: inline-block;
/* Add perspective for enhanced depth */
transition: transform 0.15s ease-out;
will-change: transform, box-shadow;
}
.tilt-image {
display: block;
width: 100%;
height: auto;
border-radius: 4px;
transform-style: preserve-3d;
}
</style>