add motion in chart
This commit is contained in:
@@ -203,7 +203,9 @@
|
|||||||
|
|
||||||
<div class="dlanding-chart">
|
<div class="dlanding-chart">
|
||||||
<div
|
<div
|
||||||
|
ref="heroChartPlotEl"
|
||||||
class="dlanding-chart__plot"
|
class="dlanding-chart__plot"
|
||||||
|
:class="{ 'is-drawn': heroChartDrawn }"
|
||||||
@pointermove="onHeroChartPointerMove"
|
@pointermove="onHeroChartPointerMove"
|
||||||
@pointerleave="onHeroChartPointerLeave"
|
@pointerleave="onHeroChartPointerLeave"
|
||||||
>
|
>
|
||||||
@@ -227,20 +229,23 @@
|
|||||||
<path
|
<path
|
||||||
class="dlanding-chart__path"
|
class="dlanding-chart__path"
|
||||||
:d="landingChart.linePath"
|
:d="landingChart.linePath"
|
||||||
|
pathLength="1"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#c98a5f"
|
stroke="#c98a5f"
|
||||||
stroke-width="3"
|
stroke-width="3"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle :cx="landingChart.lastX" :cy="landingChart.lastY" r="5" fill="#c98a5f" />
|
<g class="dlanding-chart__endpoint">
|
||||||
<circle
|
<circle :cx="landingChart.lastX" :cy="landingChart.lastY" r="5" fill="#c98a5f" />
|
||||||
:cx="landingChart.lastX"
|
<circle
|
||||||
:cy="landingChart.lastY"
|
:cx="landingChart.lastX"
|
||||||
r="9"
|
:cy="landingChart.lastY"
|
||||||
fill="#c98a5f"
|
r="9"
|
||||||
fill-opacity="0.2"
|
fill="#c98a5f"
|
||||||
/>
|
fill-opacity="0.2"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<div v-else class="dlanding-chart__empty">
|
<div v-else class="dlanding-chart__empty">
|
||||||
{{ liveChartLoading ? 'در حال بارگذاری نمودار…' : 'نمودار در دسترس نیست' }}
|
{{ liveChartLoading ? 'در حال بارگذاری نمودار…' : 'نمودار در دسترس نیست' }}
|
||||||
@@ -559,7 +564,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
ref="liveChartPlotEl"
|
||||||
class="dlanding-live__chart"
|
class="dlanding-live__chart"
|
||||||
|
:class="{ 'is-drawn': liveChartDrawn }"
|
||||||
@pointermove="onLiveChartPointerMove"
|
@pointermove="onLiveChartPointerMove"
|
||||||
@pointerleave="onLiveChartPointerLeave"
|
@pointerleave="onLiveChartPointerLeave"
|
||||||
>
|
>
|
||||||
@@ -576,25 +583,30 @@
|
|||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<path
|
<path
|
||||||
|
class="dlanding-live__chart-area"
|
||||||
:d="liveSectionChart.areaPath"
|
:d="liveSectionChart.areaPath"
|
||||||
fill="url(#liveChartFill)"
|
fill="url(#liveChartFill)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
|
class="dlanding-live__chart-path"
|
||||||
:d="liveSectionChart.linePath"
|
:d="liveSectionChart.linePath"
|
||||||
|
pathLength="1"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="#c98a5f"
|
stroke="#c98a5f"
|
||||||
stroke-width="3"
|
stroke-width="3"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle :cx="liveSectionChart.lastX" :cy="liveSectionChart.lastY" r="5" fill="#c98a5f" />
|
<g class="dlanding-live__chart-endpoint">
|
||||||
<circle
|
<circle :cx="liveSectionChart.lastX" :cy="liveSectionChart.lastY" r="5" fill="#c98a5f" />
|
||||||
:cx="liveSectionChart.lastX"
|
<circle
|
||||||
:cy="liveSectionChart.lastY"
|
:cx="liveSectionChart.lastX"
|
||||||
r="10"
|
:cy="liveSectionChart.lastY"
|
||||||
fill="#c98a5f"
|
r="10"
|
||||||
fill-opacity="0.2"
|
fill="#c98a5f"
|
||||||
/>
|
fill-opacity="0.2"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
<div v-else class="dlanding-live__chart-empty">
|
<div v-else class="dlanding-live__chart-empty">
|
||||||
{{
|
{{
|
||||||
@@ -978,8 +990,12 @@ const canPromptInstall = ref(false)
|
|||||||
/** اپ از قبل نصب شده یا در حالت standalone اجرا میشود */
|
/** اپ از قبل نصب شده یا در حالت standalone اجرا میشود */
|
||||||
const isAppInstalledOrStandalone = ref(false)
|
const isAppInstalledOrStandalone = ref(false)
|
||||||
const statsMetricsEl = ref<HTMLElement | null>(null)
|
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 metricAnimatedValues = ref<number[]>([0, 0, 0, 0])
|
||||||
const metricsCountStarted = ref(false)
|
const metricsCountStarted = ref(false)
|
||||||
|
const heroChartDrawn = ref(false)
|
||||||
|
const liveChartDrawn = ref(false)
|
||||||
const projectName = computed(() => config.config?.name_fa ?? 'بورسمس')
|
const projectName = computed(() => config.config?.name_fa ?? 'بورسمس')
|
||||||
const patternMaskStyle = computed(() => ({
|
const patternMaskStyle = computed(() => ({
|
||||||
WebkitMaskImage: `url(${heroPattern})`,
|
WebkitMaskImage: `url(${heroPattern})`,
|
||||||
@@ -1465,6 +1481,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
void nextTick(() => {
|
void nextTick(() => {
|
||||||
setupMetricsCountUp()
|
setupMetricsCountUp()
|
||||||
|
setupHeroChartDraw()
|
||||||
|
setupLiveChartDraw()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1475,6 +1493,10 @@ onBeforeUnmount(() => {
|
|||||||
syncBodyScrollLock(false)
|
syncBodyScrollLock(false)
|
||||||
metricsObserver?.disconnect()
|
metricsObserver?.disconnect()
|
||||||
metricsObserver = undefined
|
metricsObserver = undefined
|
||||||
|
heroChartObserver?.disconnect()
|
||||||
|
heroChartObserver = undefined
|
||||||
|
liveChartObserver?.disconnect()
|
||||||
|
liveChartObserver = undefined
|
||||||
if (metricsRaf) cancelAnimationFrame(metricsRaf)
|
if (metricsRaf) cancelAnimationFrame(metricsRaf)
|
||||||
if (pricePollTimer) {
|
if (pricePollTimer) {
|
||||||
clearInterval(pricePollTimer)
|
clearInterval(pricePollTimer)
|
||||||
@@ -1513,8 +1535,87 @@ const displayMetrics = computed(() =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
let metricsObserver: IntersectionObserver | undefined
|
let metricsObserver: IntersectionObserver | undefined
|
||||||
|
let heroChartObserver: IntersectionObserver | undefined
|
||||||
|
let liveChartObserver: IntersectionObserver | undefined
|
||||||
let metricsRaf = 0
|
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() {
|
function isStandaloneDisplay() {
|
||||||
return (
|
return (
|
||||||
window.matchMedia('(display-mode: standalone)').matches ||
|
window.matchMedia('(display-mode: standalone)').matches ||
|
||||||
|
|||||||
@@ -597,6 +597,33 @@ body.dlanding-menu-open {
|
|||||||
height: clamp(80px, 10vw, 96px);
|
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 {
|
&__empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 {
|
.dlanding-order-card {
|
||||||
width: 556px;
|
width: 556px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -1517,6 +1586,33 @@ body.dlanding-menu-open {
|
|||||||
height: 100%;
|
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 {
|
&__chart-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user