|
@@ -0,0 +1,326 @@
|
|
|
|
+<template>
|
|
|
|
+ <div class="painting-detail-for-shuang-gou">
|
|
|
|
+ <div
|
|
|
|
+ class="painting-wrap"
|
|
|
|
+ :style="{
|
|
|
|
+ clipPath: `rect(0 100% ${AnimationProgress.value}% 0)`,
|
|
|
|
+ }"
|
|
|
|
+ >
|
|
|
|
+ <!-- <img
|
|
|
|
+ class=""
|
|
|
|
+ src="@/assets/images/"
|
|
|
|
+ alt=""
|
|
|
|
+ draggable="false"
|
|
|
|
+ > -->
|
|
|
|
+ <Transition name="fade-out">
|
|
|
|
+ <img
|
|
|
|
+ v-show="isAnimating"
|
|
|
|
+ class="bottom-border-for-animation"
|
|
|
|
+ :style="{
|
|
|
|
+ bottom: `${100 - AnimationProgress.value}%`,
|
|
|
|
+ }"
|
|
|
|
+ src="@/assets/images/painting-border-bottom.png"
|
|
|
|
+ alt=""
|
|
|
|
+ draggable="false"
|
|
|
|
+ >
|
|
|
|
+ </Transition>
|
|
|
|
+ </div>
|
|
|
|
+ <div
|
|
|
|
+ ref="descTextEl"
|
|
|
|
+ class="desc-text"
|
|
|
|
+ :style="{
|
|
|
|
+ opacity: isAnimating ? AnimationProgress.value / 100 : 1,
|
|
|
|
+ }"
|
|
|
|
+ >
|
|
|
|
+ <h1>{{ props.title }}</h1>
|
|
|
|
+ <p class="subtitle">
|
|
|
|
+ {{ `${props.author} (${props.age})` }}
|
|
|
|
+ </p>
|
|
|
|
+ <p class="subtitle">
|
|
|
|
+ {{ props.subtitle }}
|
|
|
|
+ </p>
|
|
|
|
+ <p class="subtitle">
|
|
|
|
+ {{ props.location }}
|
|
|
|
+ </p>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <OperationTip
|
|
|
|
+ v-if="needOperationTip"
|
|
|
|
+ class="operation-tip"
|
|
|
|
+ text=""
|
|
|
|
+ :is-show="isShowOperationTip"
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <BtnBack
|
|
|
|
+ v-if="canClose"
|
|
|
|
+ @click="emit('close')"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script setup>
|
|
|
|
+import { ref, computed, watch, onMounted, inject, onUnmounted } from "vue"
|
|
|
|
+import { useRoute, useRouter } from "vue-router"
|
|
|
|
+import { useStore } from "vuex"
|
|
|
|
+import useSizeAdapt from "@/useFunctions/useSizeAdapt"
|
|
|
|
+import TWEEN from '@tweenjs/tween.js'
|
|
|
|
+import { api as viewerApi } from 'v-viewer'
|
|
|
|
+
|
|
|
|
+const route = useRoute()
|
|
|
|
+const router = useRouter()
|
|
|
|
+const store = useStore()
|
|
|
|
+
|
|
|
|
+const $env = inject('$env')
|
|
|
|
+
|
|
|
|
+const emit = defineEmits(['close'])
|
|
|
|
+
|
|
|
|
+const {
|
|
|
|
+ windowSizeInCssForRef,
|
|
|
|
+ windowSizeWhenDesignForRef,
|
|
|
|
+} = useSizeAdapt()
|
|
|
|
+
|
|
|
|
+const props = defineProps({
|
|
|
|
+ thumb: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ bigPainting: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ title: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ author: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ // subtitle: {
|
|
|
|
+ // type: String,
|
|
|
|
+ // required: true,
|
|
|
|
+ // },
|
|
|
|
+ age: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ location: {
|
|
|
|
+ type: String,
|
|
|
|
+ required: true,
|
|
|
|
+ },
|
|
|
|
+ paintingDesc: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: '',
|
|
|
|
+ },
|
|
|
|
+ authorDesc: {
|
|
|
|
+ type: String,
|
|
|
|
+ default: '',
|
|
|
|
+ },
|
|
|
|
+ canClose: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: true,
|
|
|
|
+ },
|
|
|
|
+ size: {
|
|
|
|
+ type: Object,
|
|
|
|
+ default: () => {
|
|
|
|
+ return {
|
|
|
|
+ width: 1,
|
|
|
|
+ height: 1,
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ needOperationTip: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ default: false,
|
|
|
|
+ },
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 操作提示
|
|
|
|
+ */
|
|
|
|
+const needOperationTip = computed(() => {
|
|
|
|
+ return props.needOperationTip
|
|
|
|
+})
|
|
|
|
+const isShowOperationTip = ref(true)
|
|
|
|
+const descTextEl = ref(null)
|
|
|
|
+const descTextElScrollTop = ref(0)
|
|
|
|
+onMounted(() => {
|
|
|
|
+ descTextEl.value.addEventListener('scroll', (e) => {
|
|
|
|
+ descTextElScrollTop.value = descTextEl.value.scrollTop
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+})
|
|
|
|
+const unwatch = watch(descTextElScrollTop, (v) => {
|
|
|
|
+ isShowOperationTip.value = false
|
|
|
|
+ unwatch()
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+const isAnimating = ref(true)
|
|
|
|
+
|
|
|
|
+/** 卷轴展开动画的tweening */
|
|
|
|
+const AnimationProgress = ref({
|
|
|
|
+ value: 7
|
|
|
|
+})
|
|
|
|
+const tween = new TWEEN.Tween(AnimationProgress.value)
|
|
|
|
+tween.to({
|
|
|
|
+ value: 100,
|
|
|
|
+}, 3000)
|
|
|
|
+tween.easing(TWEEN.Easing.Cubic.InOut)
|
|
|
|
+let animationRequestId = null
|
|
|
|
+const animate = () => {
|
|
|
|
+ animationRequestId = requestAnimationFrame(animate)
|
|
|
|
+ TWEEN.update()
|
|
|
|
+}
|
|
|
|
+// tween.onUpdate(function (object) {
|
|
|
|
+// console.log(object.value)
|
|
|
|
+// })
|
|
|
|
+onMounted(() => {
|
|
|
|
+ tween.start()
|
|
|
|
+ animate()
|
|
|
|
+})
|
|
|
|
+tween.onComplete(() => {
|
|
|
|
+ isAnimating.value = false
|
|
|
|
+ cancelAnimationFrame(animationRequestId)
|
|
|
|
+})
|
|
|
|
+onUnmounted(() => {
|
|
|
|
+ tween.stop()
|
|
|
|
+ cancelAnimationFrame(animationRequestId)
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 尺寸相关
|
|
|
|
+ */
|
|
|
|
+const paintingWrapWidth = ref(268)
|
|
|
|
+const paintingWrapHeight = ref(426)
|
|
|
|
+let wrapSizeRatio = paintingWrapWidth.value / paintingWrapHeight.value
|
|
|
|
+if (wrapSizeRatio < 1) {
|
|
|
|
+ wrapSizeRatio = 1 / wrapSizeRatio
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+let sizeRatio = props.size.width / props.size.height
|
|
|
|
+if (sizeRatio < 1) {
|
|
|
|
+ sizeRatio = 1 / sizeRatio
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+const isOversize = ref(sizeRatio > wrapSizeRatio)
|
|
|
|
+
|
|
|
|
+const paintingWrap2El = ref(null)
|
|
|
|
+const paintingEl = ref(null)
|
|
|
|
+onMounted(() => {
|
|
|
|
+ if (isOversize.value) {
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ const y = (paintingEl.value.clientHeight - paintingWrap2El.value.clientHeight) / 2
|
|
|
|
+ paintingWrap2El.value.scrollTo({
|
|
|
|
+ top: y,
|
|
|
|
+ // behavior: 'smooth',
|
|
|
|
+ })
|
|
|
|
+ }, 0)
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+function showBigPainting() {
|
|
|
|
+ viewerApi({
|
|
|
|
+ images: [props.bigPainting],
|
|
|
|
+ })
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="less" scoped>
|
|
|
|
+.painting-detail-for-shuang-gou{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ ::-webkit-scrollbar { width: 0; height: 0; }
|
|
|
|
+ >.painting-wrap{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 50%;
|
|
|
|
+ top: calc(70 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ transform: translate(-50%, 0);
|
|
|
|
+ width: calc(356 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ height: calc(602 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ >img.painting-border{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 0;
|
|
|
|
+ top: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ }
|
|
|
|
+ >.painting-wrap-2{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 50%;
|
|
|
|
+ top: calc(110 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ transform: translate(-50%, 0);
|
|
|
|
+ width: calc(v-bind('paintingWrapWidth') / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ height: calc(v-bind('paintingWrapHeight') / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ overflow: auto;
|
|
|
|
+ >img.painting{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 50%;
|
|
|
|
+ top: 50%;
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
+ width: 100%;
|
|
|
|
+ }
|
|
|
|
+ >img.painting.oversize{
|
|
|
|
+ position: static;
|
|
|
|
+ left: initial;
|
|
|
|
+ top: initial;
|
|
|
|
+ transform: initial;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ >img.bottom-border-for-animation{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 0;
|
|
|
|
+ width: 100%;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ >.desc-text{
|
|
|
|
+ position: absolute;
|
|
|
|
+ left: 50%;
|
|
|
|
+ bottom: 2%;
|
|
|
|
+ transform: translateX(-50%);
|
|
|
|
+ width: calc(306 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ height: calc(130 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ overflow: auto;
|
|
|
|
+ >h1{
|
|
|
|
+ text-align: center;
|
|
|
|
+ font-family: KaiTi, KaiTi;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ font-size: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ color: #FFFFFF;
|
|
|
|
+ margin-bottom: calc(19 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ }
|
|
|
|
+ >p.subtitle{
|
|
|
|
+ text-align: center;
|
|
|
|
+ font-family: KaiTi, KaiTi;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ font-size: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ color: rgba(255, 255, 255, 0.8);
|
|
|
|
+ line-height: calc(19 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ margin-bottom: calc(6 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ }
|
|
|
|
+ >h2{
|
|
|
|
+ display: inline-block;
|
|
|
|
+ margin-top: calc(30 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ font-family: KaiTi, KaiTi;
|
|
|
|
+ color: #FFFFFF;
|
|
|
|
+ margin-top: 2em;
|
|
|
|
+ margin-bottom: 0.5em;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ font-size: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ }
|
|
|
|
+ >.normal-text{
|
|
|
|
+ font-family: KaiTi, KaiTi;
|
|
|
|
+ color: #FFFFFF;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ font-size: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ line-height: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
|
|
|
|
+ text-align: justify;
|
|
|
|
+ white-space: pre-line;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|