add motion in chart
This commit is contained in:
@@ -203,7 +203,9 @@
|
||||
|
||||
<div class="dlanding-chart">
|
||||
<div
|
||||
ref="heroChartPlotEl"
|
||||
class="dlanding-chart__plot"
|
||||
:class="{ 'is-drawn': heroChartDrawn }"
|
||||
@pointermove="onHeroChartPointerMove"
|
||||
@pointerleave="onHeroChartPointerLeave"
|
||||
>
|
||||
@@ -227,20 +229,23 @@
|
||||
<path
|
||||
class="dlanding-chart__path"
|
||||
:d="landingChart.linePath"
|
||||
pathLength="1"
|
||||
fill="none"
|
||||
stroke="#c98a5f"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<circle :cx="landingChart.lastX" :cy="landingChart.lastY" r="5" fill="#c98a5f" />
|
||||
<circle
|
||||
:cx="landingChart.lastX"
|
||||
:cy="landingChart.lastY"
|
||||
r="9"
|
||||
fill="#c98a5f"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<g class="dlanding-chart__endpoint">
|
||||
<circle :cx="landingChart.lastX" :cy="landingChart.lastY" r="5" fill="#c98a5f" />
|
||||
<circle
|
||||
:cx="landingChart.lastX"
|
||||
:cy="landingChart.lastY"
|
||||
r="9"
|
||||
fill="#c98a5f"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div v-else class="dlanding-chart__empty">
|
||||
{{ liveChartLoading ? 'در حال بارگذاری نمودار…' : 'نمودار در دسترس نیست' }}
|
||||
@@ -559,7 +564,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref="liveChartPlotEl"
|
||||
class="dlanding-live__chart"
|
||||
:class="{ 'is-drawn': liveChartDrawn }"
|
||||
@pointermove="onLiveChartPointerMove"
|
||||
@pointerleave="onLiveChartPointerLeave"
|
||||
>
|
||||
@@ -576,25 +583,30 @@
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
class="dlanding-live__chart-area"
|
||||
:d="liveSectionChart.areaPath"
|
||||
fill="url(#liveChartFill)"
|
||||
/>
|
||||
<path
|
||||
class="dlanding-live__chart-path"
|
||||
:d="liveSectionChart.linePath"
|
||||
pathLength="1"
|
||||
fill="none"
|
||||
stroke="#c98a5f"
|
||||
stroke-width="3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<circle :cx="liveSectionChart.lastX" :cy="liveSectionChart.lastY" r="5" fill="#c98a5f" />
|
||||
<circle
|
||||
:cx="liveSectionChart.lastX"
|
||||
:cy="liveSectionChart.lastY"
|
||||
r="10"
|
||||
fill="#c98a5f"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<g class="dlanding-live__chart-endpoint">
|
||||
<circle :cx="liveSectionChart.lastX" :cy="liveSectionChart.lastY" r="5" fill="#c98a5f" />
|
||||
<circle
|
||||
:cx="liveSectionChart.lastX"
|
||||
:cy="liveSectionChart.lastY"
|
||||
r="10"
|
||||
fill="#c98a5f"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<div v-else class="dlanding-live__chart-empty">
|
||||
{{
|
||||
@@ -978,8 +990,12 @@ const canPromptInstall = ref(false)
|
||||
/** اپ از قبل نصب شده یا در حالت standalone اجرا میشود */
|
||||
const isAppInstalledOrStandalone = ref(false)
|
||||
const statsMetricsEl = ref<HTMLElement | null>(null)
|
||||
const heroChartPlotEl = ref<HTMLElement | null>(null)
|
||||
const liveChartPlotEl = ref<HTMLElement | null>(null)
|
||||
const metricAnimatedValues = ref<number[]>([0, 0, 0, 0])
|
||||
const metricsCountStarted = ref(false)
|
||||
const heroChartDrawn = ref(false)
|
||||
const liveChartDrawn = ref(false)
|
||||
const projectName = computed(() => config.config?.name_fa ?? 'بورسمس')
|
||||
const patternMaskStyle = computed(() => ({
|
||||
WebkitMaskImage: `url(${heroPattern})`,
|
||||
@@ -1465,6 +1481,8 @@ onMounted(() => {
|
||||
|
||||
void nextTick(() => {
|
||||
setupMetricsCountUp()
|
||||
setupHeroChartDraw()
|
||||
setupLiveChartDraw()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1475,6 +1493,10 @@ onBeforeUnmount(() => {
|
||||
syncBodyScrollLock(false)
|
||||
metricsObserver?.disconnect()
|
||||
metricsObserver = undefined
|
||||
heroChartObserver?.disconnect()
|
||||
heroChartObserver = undefined
|
||||
liveChartObserver?.disconnect()
|
||||
liveChartObserver = undefined
|
||||
if (metricsRaf) cancelAnimationFrame(metricsRaf)
|
||||
if (pricePollTimer) {
|
||||
clearInterval(pricePollTimer)
|
||||
@@ -1513,8 +1535,87 @@ const displayMetrics = computed(() =>
|
||||
)
|
||||
|
||||
let metricsObserver: IntersectionObserver | undefined
|
||||
let heroChartObserver: IntersectionObserver | undefined
|
||||
let liveChartObserver: IntersectionObserver | undefined
|
||||
let metricsRaf = 0
|
||||
|
||||
function prefersReducedMotion() {
|
||||
return (
|
||||
typeof window !== 'undefined' &&
|
||||
window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
)
|
||||
}
|
||||
|
||||
function setupChartDrawOnce(
|
||||
el: HTMLElement | null,
|
||||
drawn: { value: boolean },
|
||||
getObserver: () => IntersectionObserver | undefined,
|
||||
setObserver: (observer: IntersectionObserver | undefined) => void,
|
||||
) {
|
||||
if (drawn.value) return
|
||||
if (!el) return
|
||||
|
||||
if (prefersReducedMotion() || typeof IntersectionObserver === 'undefined') {
|
||||
drawn.value = true
|
||||
return
|
||||
}
|
||||
|
||||
getObserver()?.disconnect()
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries.some((entry) => entry.isIntersecting)) {
|
||||
drawn.value = true
|
||||
observer.disconnect()
|
||||
setObserver(undefined)
|
||||
}
|
||||
},
|
||||
{ threshold: 0.35 },
|
||||
)
|
||||
setObserver(observer)
|
||||
observer.observe(el)
|
||||
}
|
||||
|
||||
function setupHeroChartDraw() {
|
||||
setupChartDrawOnce(
|
||||
heroChartPlotEl.value,
|
||||
heroChartDrawn,
|
||||
() => heroChartObserver,
|
||||
(observer) => {
|
||||
heroChartObserver = observer
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
function setupLiveChartDraw() {
|
||||
setupChartDrawOnce(
|
||||
liveChartPlotEl.value,
|
||||
liveChartDrawn,
|
||||
() => liveChartObserver,
|
||||
(observer) => {
|
||||
liveChartObserver = observer
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
watch(
|
||||
() => landingChart.value.hasData,
|
||||
async (hasData) => {
|
||||
if (!hasData || heroChartDrawn.value) return
|
||||
await nextTick()
|
||||
setupHeroChartDraw()
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => liveSectionChart.value.hasData,
|
||||
async (hasData) => {
|
||||
if (!hasData || liveChartDrawn.value) return
|
||||
await nextTick()
|
||||
setupLiveChartDraw()
|
||||
},
|
||||
)
|
||||
|
||||
function isStandaloneDisplay() {
|
||||
return (
|
||||
window.matchMedia('(display-mode: standalone)').matches ||
|
||||
|
||||
@@ -597,6 +597,33 @@ body.dlanding-menu-open {
|
||||
height: clamp(80px, 10vw, 96px);
|
||||
}
|
||||
|
||||
&__path {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
}
|
||||
|
||||
&__area {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__endpoint {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__plot.is-drawn {
|
||||
.dlanding-chart__path {
|
||||
animation: dlanding-chart-draw 1.45s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
}
|
||||
|
||||
.dlanding-chart__area {
|
||||
animation: dlanding-chart-fade 0.55s ease-out 0.75s forwards;
|
||||
}
|
||||
|
||||
.dlanding-chart__endpoint {
|
||||
animation: dlanding-chart-fade 0.35s ease-out 1.25s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&__empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -677,6 +704,48 @@ body.dlanding-menu-open {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dlanding-chart-draw {
|
||||
to {
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dlanding-chart-fade {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.dlanding-chart {
|
||||
&__path {
|
||||
stroke-dasharray: none;
|
||||
stroke-dashoffset: 0;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
&__area,
|
||||
&__endpoint {
|
||||
opacity: 1;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dlanding-live {
|
||||
&__chart-path {
|
||||
stroke-dasharray: none;
|
||||
stroke-dashoffset: 0;
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
&__chart-area,
|
||||
&__chart-endpoint {
|
||||
opacity: 1;
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dlanding-order-card {
|
||||
width: 556px;
|
||||
max-width: 100%;
|
||||
@@ -1517,6 +1586,33 @@ body.dlanding-menu-open {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__chart-path {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
}
|
||||
|
||||
&__chart-area {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__chart-endpoint {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&__chart.is-drawn {
|
||||
.dlanding-live__chart-path {
|
||||
animation: dlanding-chart-draw 1.55s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
}
|
||||
|
||||
.dlanding-live__chart-area {
|
||||
animation: dlanding-chart-fade 0.55s ease-out 0.85s forwards;
|
||||
}
|
||||
|
||||
.dlanding-live__chart-endpoint {
|
||||
animation: dlanding-chart-fade 0.35s ease-out 1.35s forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&__chart-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user