Merge branch 'main' into feat/scroll-stack

This commit is contained in:
David
2025-07-23 22:09:11 +03:00
committed by GitHub
10 changed files with 660 additions and 13 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ import Ribbons from '../../content/Animations/Ribbons/Ribbons.vue';
import { ribbons } from '@/constants/code/Animations/ribbonsCode';
const baseThickness = ref(30);
const colors = ref(['#5227FF']);
const colors = ref(['#27FF64']);
const speedMultiplier = ref(0.5);
const maxAge = ref(500);
const enableFade = ref(false);
+151
View File
@@ -0,0 +1,151 @@
<template>
<TabbedLayout>
<template #preview>
<div
class="flex-col bg-[linear-gradient(to_bottom,_#060010,_#0D0716,_#0D0716,_#060010)] h-[500px] overflow-hidden demo-container"
>
<StickerPeel
:image-src="logo"
:rotate="rotate"
:width="width"
:peel-back-hover-pct="peelBackHoverPct"
:peel-back-active-pct="peelBackActivePct"
:lighting-intensity="lightingIntensity"
:shadow-intensity="shadowIntensity"
:peel-direction="peelDirection"
class-name="z-2 absolute"
/>
<p
class="top-[1em] left-1/2 z-0 absolute font-black text-[#222] text-[clamp(1.5rem,4vw,3rem)] -translate-x-1/2 transform"
>
Try dragging it!
</p>
</div>
<Customize>
<PreviewSlider title="Peel Direction" :min="0" :max="360" :step="1" value-unit="°" v-model="peelDirection" />
<PreviewSlider title="Rotate" :min="0" :max="60" :step="1" value-unit="°" v-model="rotate" />
<PreviewSlider title="Width" :min="100" :max="300" :step="10" value-unit="px" v-model="width" />
<PreviewSlider title="Peel Hover %" :min="0" :max="50" :step="1" value-unit="%" v-model="peelBackHoverPct" />
<PreviewSlider title="Peel Active %" :min="0" :max="70" :step="1" value-unit="%" v-model="peelBackActivePct" />
<PreviewSlider title="Lighting Intensity" :min="0" :max="0.5" :step="0.01" v-model="lightingIntensity" />
<PreviewSlider title="Shadow Intensity" :min="0" :max="1" :step="0.01" v-model="shadowIntensity" />
</Customize>
<PropTable :data="propData" />
<Dependencies :dependency-list="['gsap']" />
</template>
<template #code>
<CodeExample :code-object="stickerPeel" />
</template>
<template #cli>
<CliInstallation :command="stickerPeel.cli" />
</template>
</TabbedLayout>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import CliInstallation from '../../components/code/CliInstallation.vue';
import CodeExample from '../../components/code/CodeExample.vue';
import Dependencies from '../../components/code/Dependencies.vue';
import Customize from '../../components/common/Customize.vue';
import PreviewSlider from '../../components/common/PreviewSlider.vue';
import PropTable from '../../components/common/PropTable.vue';
import TabbedLayout from '../../components/common/TabbedLayout.vue';
import { stickerPeel } from '../../constants/code/Animations/stickerPeelCode';
import StickerPeel from '../../content/Animations/StickerPeel/StickerPeel.vue';
import logo from '../../assets/logos/vue-bits-sticker.png';
const rotate = ref(0);
const width = ref(200);
const peelBackHoverPct = ref(30);
const peelBackActivePct = ref(40);
const lightingIntensity = ref(0.1);
const shadowIntensity = ref(0.5);
const peelDirection = ref(0);
const propData = [
{
name: 'imageSrc',
type: 'string',
default: 'required',
description: 'The source URL for the sticker image'
},
{
name: 'rotate',
type: 'number',
default: '30',
description: 'The rotation angle in degrees when dragging'
},
{
name: 'peelBackHoverPct',
type: 'number',
default: '30',
description: 'Percentage of peel effect on hover (0-100)'
},
{
name: 'peelBackActivePct',
type: 'number',
default: '40',
description: 'Percentage of peel effect when active/clicked (0-100)'
},
{
name: 'peelDirection',
type: 'number',
default: '0',
description: 'Direction of the peel effect in degrees (0-360)'
},
{
name: 'peelEasing',
type: 'string',
default: 'power3.out',
description: 'GSAP easing function for peel animations'
},
{
name: 'peelHoverEasing',
type: 'string',
default: 'power2.out',
description: 'GSAP easing function for hover transitions'
},
{
name: 'width',
type: 'number',
default: '200',
description: 'Width of the sticker in pixels'
},
{
name: 'shadowIntensity',
type: 'number',
default: '0.6',
description: 'Intensity of the shadow effect (0-1)'
},
{
name: 'lightingIntensity',
type: 'number',
default: '0.1',
description: 'Intensity of the lighting effect (0-1)'
},
{
name: 'initialPosition',
type: 'string',
default: 'center',
description: "Initial position of the sticker ('center', 'top-left', 'top-right', 'bottom-left', 'bottom-right')"
},
{
name: 'className',
type: 'string',
default: '',
description: 'Custom class name for additional styling'
}
];
</script>
<style scoped>
.demo-container {
padding: 0;
}
</style>
+53 -10
View File
@@ -1,7 +1,7 @@
<template>
<TabbedLayout>
<template #preview>
<div class="relative overflow-y-auto no-scrollbar demo-container">
<div class="relative overflow-y-auto no-scrollbar demo-container" ref="scrollContainerRef">
<GlassSurface
:key="key"
:width="360"
@@ -22,26 +22,21 @@
/>
<div class="absolute flex flex-col items-center gap-6 top-0 left-0 right-0">
<div
class="absolute translate-y-1/2 top-12 text-4xl font-bold text-[#27FF64] z-0 whitespace-nowrap text-center"
>
<div class="absolute translate-y-1/2 top-12 text-4xl font-bold text-[#333] z-0 whitespace-nowrap text-center">
Try scrolling.
</div>
<!-- Top Spacer -->
<div class="h-60 w-full" />
<!-- Image Blocks -->
<div v-for="(item, index) in imageBlocks" :key="index" class="relative">
<div v-for="(item, index) in imageBlocks" :key="index" class="relative py-4">
<img :src="item.src" class="w-128 rounded-2xl object-cover grayscale-100" />
<div
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 font-extrabold text-center leading-[100%] text-[3rem] min-w-72"
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 font-extrabold text-center leading-[100%] text-[3rem] min-w-72 mix-blend-overlay text-white"
>
{{ item.text }}
</div>
</div>
<!-- Bottom Spacer -->
<div class="h-60 w-full" />
</div>
</div>
@@ -75,7 +70,8 @@
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import { ref, watch, onMounted, onUnmounted, useTemplateRef } from 'vue';
import Lenis from 'lenis';
import TabbedLayout from '../../components/common/TabbedLayout.vue';
import PropTable from '../../components/common/PropTable.vue';
import CliInstallation from '../../components/code/CliInstallation.vue';
@@ -88,6 +84,10 @@ import { useForceRerender } from '@/composables/useForceRerender';
const { rerenderKey: key, forceRerender } = useForceRerender();
const scrollContainerRef = useTemplateRef<HTMLElement>('scrollContainerRef');
let lenis: Lenis | null = null;
let rafId: number | null = null;
const borderRadius = ref(50);
const backgroundOpacity = ref(0.1);
const saturation = ref(1);
@@ -118,6 +118,49 @@ watch(
}
);
const initLenis = () => {
if (!scrollContainerRef.value) return;
lenis = new Lenis({
wrapper: scrollContainerRef.value,
content: scrollContainerRef.value.firstElementChild as HTMLElement,
duration: 1.2,
easing: (t: number) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
orientation: 'vertical',
gestureOrientation: 'vertical',
smoothWheel: true,
wheelMultiplier: 1,
touchMultiplier: 2,
infinite: false
});
const raf = (time: number) => {
lenis?.raf(time);
rafId = requestAnimationFrame(raf);
};
rafId = requestAnimationFrame(raf);
};
const destroyLenis = () => {
if (rafId) {
cancelAnimationFrame(rafId);
rafId = null;
}
if (lenis) {
lenis.destroy();
lenis = null;
}
};
onMounted(() => {
initLenis();
});
onUnmounted(() => {
destroyLenis();
});
const imageBlocks = [
{
src: 'https://images.unsplash.com/photo-1500673587002-1d2548cfba1b?q=80&w=1740&auto=format&fit=crop',