1<template>
2 <div
3 ref="scrollableDiv"
4 :style="`maskImage: ${maskStyle}`"
5 @scroll="updateMaskStyle"
6 >
7 <slot></slot>
8 </div>
9</template>
10<script setup lang="ts">
11import { computed, onMounted, ref } from "vue";
12
13const props = defineProps({
14 maskLength: {
15 type: Number,
16 default: 30,
17 },
18 orientation: {
19 type: String,
20 default: "vertical",
21 },
22});
23
24const scrollableDiv = ref(null);
25const maskStyle = ref("none");
26const side = computed(() =>
27 props.orientation == "horizontal" ? "right" : "bottom"
28);
29
30function updateMaskStyle() {
31 if (!scrollableDiv.value) return;
32
33 let scrollWidth = scrollableDiv.value.scrollWidth;
34 let clientWidth = scrollableDiv.value.clientWidth;
35 let scrollHeight = scrollableDiv.value.scrollHeight;
36 let clientHeight = scrollableDiv.value.clientHeight;
37 let scrollTop = scrollableDiv.value.scrollTop;
38 let scrollLeft = scrollableDiv.value.scrollLeft;
39
40 maskStyle.value = "none";
41
42 // faded on both sides
43 if (
44 (side.value == "right" && scrollWidth > clientWidth) ||
45 (side.value == "bottom" && scrollHeight > clientHeight)
46 ) {
47 maskStyle.value = `linear-gradient(to ${side.value}, transparent, black ${props.maskLength}px, black calc(100% - ${props.maskLength}px), transparent);`;
48 }
49
50 // faded on left or top
51 if (
52 (side.value == "right" && scrollLeft - 20 > clientWidth) ||
53 (side.value == "bottom" && scrollTop + clientHeight >= scrollHeight)
54 ) {
55 maskStyle.value = `linear-gradient(to ${side.value}, transparent, black ${props.maskLength}px, black 100%, transparent);`;
56 }
57
58 // faded on right or bottom
59 if (
60 (side.value == "right" && scrollLeft == 0) ||
61 (side.value == "bottom" && scrollTop == 0)
62 ) {
63 maskStyle.value = `linear-gradient(to ${side.value}, black calc(100% - ${props.maskLength}px), transparent 100%);`;
64 }
65
66 if (
67 (side.value == "right" && clientWidth == scrollWidth) ||
68 (side.value == "bottom" && clientHeight == scrollHeight)
69 ) {
70 maskStyle.value = "none";
71 }
72}
73
74onMounted(() => setTimeout(() => updateMaskStyle(), 300));
75</script>
76