From afc5438f384590929a6b94c12d46ce273d082cbe Mon Sep 17 00:00:00 2001 From: zyronon Date: Tue, 12 Dec 2023 01:57:35 +0800 Subject: [PATCH] Adapt to mobile devices --- components.d.ts | 2 + src/assets/css/style.scss | 24 +- src/components/dialog/DictDiglog.vue | 2 +- src/components/slide/SlideHorizontal.vue | 106 ++++ src/components/slide/SlideItem.vue | 18 + src/components/slide/common.js | 135 +++++ src/pages/mobile/{home.vue => Home.vue} | 0 src/pages/mobile/index.vue | 22 +- .../{practice.vue => practice/index-test.vue} | 96 +++- src/pages/mobile/practice/index.vue | 150 ++++++ .../practice/practice-word/Typing.vue | 6 +- .../practice/practice-word/TypingWord.vue | 466 ++++++++++++++++++ .../mobile/practice/practice-word/index.vue | 86 ++++ src/pages/{ => pc}/dict/DictManage.vue | 6 +- .../dict/components/ArticleDictDetail.vue | 2 +- .../dict/components/ChapterWordList.vue | 0 .../{ => pc}/dict/components/EditDict.vue | 0 .../dict/components/WordDictDetail.vue | 4 +- src/pages/{ => pc}/dict/index.vue | 6 +- src/pages/{ => pc}/practice/Footer.vue | 0 src/pages/{ => pc}/practice/Options.vue | 0 src/pages/{ => pc}/practice/Panel.vue | 0 src/pages/{ => pc}/practice/Statistics.vue | 0 src/pages/{ => pc}/practice/index.vue | 8 +- .../practice-article/TypingArticle.vue | 2 +- .../practice/practice-article/index.vue | 2 +- .../practice/practice-word/TypingWord.vue | 10 +- .../{ => pc}/practice/practice-word/index.vue | 3 +- src/router.ts | 23 +- src/types.ts | 4 + src/utils/gm.js | 113 +++++ 31 files changed, 1233 insertions(+), 63 deletions(-) create mode 100644 src/components/slide/SlideHorizontal.vue create mode 100644 src/components/slide/SlideItem.vue create mode 100644 src/components/slide/common.js rename src/pages/mobile/{home.vue => Home.vue} (100%) rename src/pages/mobile/{practice.vue => practice/index-test.vue} (78%) create mode 100644 src/pages/mobile/practice/index.vue rename src/pages/{ => mobile}/practice/practice-word/Typing.vue (97%) create mode 100644 src/pages/mobile/practice/practice-word/TypingWord.vue create mode 100644 src/pages/mobile/practice/practice-word/index.vue rename src/pages/{ => pc}/dict/DictManage.vue (94%) rename src/pages/{ => pc}/dict/components/ArticleDictDetail.vue (99%) rename src/pages/{ => pc}/dict/components/ChapterWordList.vue (100%) rename src/pages/{ => pc}/dict/components/EditDict.vue (100%) rename src/pages/{ => pc}/dict/components/WordDictDetail.vue (99%) rename src/pages/{ => pc}/dict/index.vue (88%) rename src/pages/{ => pc}/practice/Footer.vue (100%) rename src/pages/{ => pc}/practice/Options.vue (100%) rename src/pages/{ => pc}/practice/Panel.vue (100%) rename src/pages/{ => pc}/practice/Statistics.vue (100%) rename src/pages/{ => pc}/practice/index.vue (94%) rename src/pages/{ => pc}/practice/practice-article/TypingArticle.vue (99%) rename src/pages/{ => pc}/practice/practice-article/index.vue (99%) rename src/pages/{ => pc}/practice/practice-word/TypingWord.vue (98%) rename src/pages/{ => pc}/practice/practice-word/index.vue (95%) create mode 100644 src/utils/gm.js diff --git a/components.d.ts b/components.d.ts index 6338ce3e..a08d651c 100644 --- a/components.d.ts +++ b/components.d.ts @@ -58,6 +58,8 @@ declare module 'vue' { Setting: typeof import('./src/components/Setting.vue')['default'] SettingDialog: typeof import('./src/components/dialog/SettingDialog.vue')['default'] Slide: typeof import('./src/components/Slide.vue')['default'] + SlideHorizontal: typeof import('./src/components/slide/SlideHorizontal.vue')['default'] + SlideItem: typeof import('./src/components/slide/SlideItem.vue')['default'] Toolbar: typeof import('./src/components/toolbar/index.vue')['default'] Tooltip: typeof import('./src/components/Tooltip.vue')['default'] TranslateSetting: typeof import('./src/components/toolbar/TranslateSetting.vue')['default'] diff --git a/src/assets/css/style.scss b/src/assets/css/style.scss index 4bd0735f..d2e13550 100644 --- a/src/assets/css/style.scss +++ b/src/assets/css/style.scss @@ -438,4 +438,26 @@ footer { display: flex; justify-content: center; align-items: center; -} \ No newline at end of file +} + + +.slide { + height: 100%; + width: 100%; + transition: height .3s; + position: relative; + overflow: hidden; + + .slide-infinite { + z-index: 1; + margin-top: 0; + transition: all .3s; + } + + .slide-list { + height: 100%; + width: 100%; + display: flex; + position: relative; + } +} diff --git a/src/components/dialog/DictDiglog.vue b/src/components/dialog/DictDiglog.vue index 62274a0e..09171cf1 100644 --- a/src/components/dialog/DictDiglog.vue +++ b/src/components/dialog/DictDiglog.vue @@ -169,7 +169,7 @@ function changeSort(v: Sort, notice: boolean = true) { function option(type: string) { show = false setTimeout(() => { - router.push({path: '/dict', query: {type: type}}) + router.push({path: '/pc/dict', query: {type: type}}) }, 500) } diff --git a/src/components/slide/SlideHorizontal.vue b/src/components/slide/SlideHorizontal.vue new file mode 100644 index 00000000..aa5c4b98 --- /dev/null +++ b/src/components/slide/SlideHorizontal.vue @@ -0,0 +1,106 @@ + + + diff --git a/src/components/slide/SlideItem.vue b/src/components/slide/SlideItem.vue new file mode 100644 index 00000000..c97ece59 --- /dev/null +++ b/src/components/slide/SlideItem.vue @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/src/components/slide/common.js b/src/components/slide/common.js new file mode 100644 index 00000000..8d3d0427 --- /dev/null +++ b/src/components/slide/common.js @@ -0,0 +1,135 @@ +import {emitter as bus} from "@/utils/eventBus.ts"; +import Utils from '@/utils/gm.js' +import {SlideType} from "@/types.ts"; +import GM from "@/utils/gm.js"; + +export function slideInit(el, state, type) { + state.wrapper.width = GM.$getCss(el, 'width') + state.wrapper.height = GM.$getCss(el, 'height') + let els = el.children + state.wrapper.childrenLength = els.length + for (let i = 0; i < els.length; i++) { + let el = els[i] + state.slideItemsWidths.push(GM.$getCss(el, 'width')) + } + + let t = getSlideDistance(state, type) + let dx1 = 0, dx2 = 0 + if (type === SlideType.HORIZONTAL) dx1 = t + else dx2 = t + + console.log('start', dx1) + Utils.$setCss(el, 'transform', `translate3d(${dx1}px, ${dx2}px, 0)`) +} + +export function slideTouchStart(e, el, state) { + Utils.$setCss(el, 'transition-duration', `0ms`) + state.start.x = e.touches[0].pageX + state.start.y = e.touches[0].pageY + state.start.time = Date.now() +} + +export function canSlide(state, judgeValue, type = SlideType.HORIZONTAL) { + if (state.needCheck) { + if (Math.abs(state.move.x) > judgeValue || Math.abs(state.move.y) > judgeValue) { + let angle = (Math.abs(state.move.x) * 10) / (Math.abs(state.move.y) * 10) + state.next = type === SlideType.HORIZONTAL ? angle > 1 : angle <= 1; + // console.log(angle) + state.needCheck = false + } else { + return false + } + } + return state.next +} + +export function slideTouchMove(e, el, state, judgeValue, canNextCb, nextCb, type = SlideType.HORIZONTAL, notNextCb) { + state.move.x = e.touches[0].pageX - state.start.x + state.move.y = e.touches[0].pageY - state.start.y + + let isNext = type === SlideType.HORIZONTAL ? state.move.x < 0 : state.move.y < 0 + + let canSlideRes = canSlide(state, judgeValue, type) + + if (canSlideRes && state.localIndex === 0 && !isNext && type === SlideType.VERTICAL) { + bus.emit(state.name + '-moveY', state.move.y) + } + if (!canNextCb?.(isNext, state.move)) return + + if (canSlideRes) { + nextCb?.() + if (type === SlideType.HORIZONTAL) { + bus.emit(state.name + '-moveX', state.move.x) + } + Utils.$stopPropagation(e) + let t = getSlideDistance(state, type) + (isNext ? judgeValue : -judgeValue) + let dx1 = 0 + let dx2 = 0 + if (type === SlideType.HORIZONTAL) { + dx1 = t + state.move.x + } else { + dx2 = t + state.move.y + } + Utils.$setCss(el, 'transition-duration', `0ms`) + Utils.$setCss(el, 'transform', `translate3d(${dx1}px, ${dx2}px, 0)`) + } else { + notNextCb?.() + } +} + +export function slideTouchEnd(e, state, canNextCb, nextCb, notNextCb, type = SlideType.HORIZONTAL) { + let isHorizontal = type === SlideType.HORIZONTAL; + let isNext = isHorizontal ? state.move.x < 0 : state.move.y < 0 + + if (!canNextCb?.(isNext)) return notNextCb?.() + if (state.next) { + Utils.$stopPropagation(e) + let endTime = Date.now() + let gapTime = endTime - state.start.time + let distance = isHorizontal ? state.move.x : state.move.y + let judgeValue = isHorizontal ? state.wrapper.width : state.wrapper.height + if (Math.abs(distance) < 20) gapTime = 1000 + if (Math.abs(distance) > (judgeValue / 3)) gapTime = 100 + if (gapTime < 150) { + if (isNext) { + state.localIndex++ + } else { + state.localIndex-- + } + return nextCb?.(isNext) + } + } + notNextCb?.() +} + +export function slideReset(el, state, type, emit) { + Utils.$setCss(el, 'transition-duration', `300ms`) + let t = getSlideDistance(state, type) + let dx1 = 0 + let dx2 = 0 + if (type === SlideType.HORIZONTAL) { + bus.emit(state.name + '-end', state.localIndex) + dx1 = t + } else { + bus.emit(state.name + '-end',) + dx2 = t + } + Utils.$setCss(el, 'transform', `translate3d(${dx1}px, ${dx2}px, 0)`) + state.start.x = state.start.y = state.start.time = state.move.x = state.move.y = 0 + state.next = false + state.needCheck = true + emit?.('update:index', state.localIndex) +} + +export function getSlideDistance(state, type = SlideType.HORIZONTAL) { + if (type === SlideType.HORIZONTAL) { + return state.slideItemsWidths.reduce((p, c, i) => { + if (i <= state.localIndex && i > 0) p -= c + return p + }, 0) + } else { + //TODO 这里需要改的和上面一样,不然不能显示半屏的div + return -state.localIndex * state.wrapper.height + } +} + diff --git a/src/pages/mobile/home.vue b/src/pages/mobile/Home.vue similarity index 100% rename from src/pages/mobile/home.vue rename to src/pages/mobile/Home.vue diff --git a/src/pages/mobile/index.vue b/src/pages/mobile/index.vue index 82a1ae51..45910fe8 100644 --- a/src/pages/mobile/index.vue +++ b/src/pages/mobile/index.vue @@ -1,22 +1,36 @@