基于 Vue3打造前台+中台通用提效解决方案(中)

33、实现全屏展示功能

我们知道在原生dom上,提供了一些方法来供我们开启或关闭全屏:

一般浏览器

使用requestFullscreen()exitFullscreen()来实现

早期版本Chrome浏览器

基于WebKit内核的浏览器需要添加webkit前缀,使用webkitRequestFullScreen()webkitCancelFullScreen()来实现。

早期版本IE浏览器

基于Trident内核的浏览器需要添加ms前缀,使用msRequestFullscreen()msExitFullscreen()来实现,注意方法里的screen的s为小写形式。

早期版本火狐浏览器

基于Gecko内核的浏览器需要添加moz前缀,使用mozRequestFullScreen()mozCancelFullScreen()来实现。

早期版本Opera浏览器

Opera浏览器需要添加o前缀,使用oRequestFullScreen()oCancelFullScreen()来实现。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>萌狼蓝天 伴姬一生</title>
</head>

<body>
    <div>
        <img src="./source/img/dog.jpg" height="300" alt="">
        <button id="full">全屏显示</button>
        <button id="cancelFull">取消全屏</button>
        <button id="isFull">是否全屏</button>
        <p id="tip" style="color:blue"></p>
    </div>
    <script>
        //全屏显示
        var div = document.querySelector('div');
        document.querySelector('#full').onclick = function () {
     
            if (div.requestFullscreen) {
     
                div.requestFullscreen(); // 正常浏览器 
            } else if (div.webkitRequestFullScreen) {
     
                div.webkitRequestFullScreen(); // webkit 
            } else if (div.mozRequestFullScreen) {
     
                div.mozRequestFullScreen(); //早期火狐浏览器
            } else if (div.oRequestFullScreen) {
     
                div.oRequestFullScreen(); //早期Opera浏览器
            } else if (div.msRequestFullscreen) {
     
                div.msRequestFullscreen(); //早期IE浏览器
            } else {
     
                alert('暂不支持在您的浏览器中全屏');
            }
        };
        //取消全屏显示
        document.querySelector('#cancelFull').onclick = function () {
     
            if (document.exitFullscreen) {
     
                document.exitFullscreen(); // 正常浏览器 
            } else if (document.webkitCancelFullScreen) {
     
                document.webkitCancelFullScreen(); // webkit 
            } else if (document.mozCancelFullScreen) {
     
                document.mozCancelFullScreen(); //早期火狐浏览器
            } else if (document.oCancelFullScreen) {
     
                document.oCancelFullScreen(); //早期Opera浏览器
            } else if (document.msCancelFullscreen) {
     
                document.msCancelFullscreen(); //早期IE浏览器
            } else {
     
                alert('暂不支持在您的浏览器中全屏');
            }
            //可以用document,也可以用上方设置的变量 div
        };
        //检测当前是否处于全屏状态
        document.querySelector('#isFull').onclick = function () {
     
            // alert(document.webkitIsFullScreen); // webkit
            // 使用上面的弹窗方式。如果是处于全屏状态,会自动退出
            document.getElementById('tip').innerHTML=document.webkitIsFullScreen;
        };
    
    </script>
</body>

</html>

但是这些方法:在一些低版本浏览器中存在兼容性的问题,需要我们手动封装;如果不想封装的话也可以使用第三方封装好的库来处理:

常见的第三方全屏库:

  • 1、vueUse
import {
    useFullscreen } from '@vueuse/core'

const imgEle = ref(null)
const {
    isFullscreen, enter, exit, toggle } = useFullscreen(imgEle)
const handleFullScreen = () => {
   
  imgEle.value.style.backgroundColor = 'transparent'
  enter()
}

34、从首页跳转到详情页解决方案

34.1、需求分析

首先我们看一下首页的图片

image-20220902162832479

分析:

  • 当点击某一个图片时、跳转到对应图片的详情页
  • 并且在跳转的过程中有从小到放大的动画的效果(类似于全屏效果的动画)
34.2、分析现阶段路由跳转动画

vue-router页面跳转如果要实现跳转到动画,需要借助于transition组件来进行实现动画

<router-view v-slot="{ Component, route }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>

image-20220902164012102

这是在vue官网截的图,从图中我们可以得知transition组件一般适用于 组件 或 元素的显示和隐藏、并不适合我们的需求、

34.3、提出解决方案

那么根据咱们上一小节的分析,我们知道通过 vue-router 的过渡动效是无法实现咱们期望的路由切换效果的。

那么我们应该如何去做呢?

想要搞明白咱们的可行性方案,那么首先我们得先来搞清楚什么是路由的跳转?

所谓路由的跳转无非指的是两个部分:

  • 1.浏览器的url 发生了变化

  • 2.浏览器中展示的页面组件发生了变化

那么只要满足这两点,我们就认为路径进行了跳转

所以说,我们是不是可以换个思路,我们不去进行真实的路由跳转,而是先修改浏览器的URL,再切换展示的页面(以组件的形式覆盖整个浏览器可视区域)。这样对于用户而言,是不是就完成了整个的路由跳转工作。

所以说我们的具体问题就变成了:

  • 1.如何让浏览器的url发生变化,但是不跳转页面
  • 2.如何让一个新的组件以包含动画的形式进行展示
    • 那么想要完成第一个功能我们可以使用:History.pushState()方法
    • 而第二个功能我们可以使用 GSAP这个动画库来进行实现。
34.4、关于GSAP介绍

GSAP, 它是一个非常强大的js动画库, 他支持Flip、滚动动画等;在其内部给我们提供了非常多的方法供我们来使用;

本次我们使用到的api,只有setto两个:

  • set: 给元素设置初始化(动画执行之前)的属性

  • to: 给元素设置结束时(动画之后结束)的属性

    • to方法的返回值为tween对象、我们通过调用对应的api来控制元素动画的开启、暂停、翻转、重新开始…

      tween.play()
      tween.pause()
      tween.resume()
      tween.reverse()
      tween.restart()
      

测试1 - 自动执行动画

<template>
    <div  class="w-screen h-[400px] flex items-center justify-center">
      <div ref="testGsap" class="border border-zinc-300 rounded-sm p-4">test GSAP</div>
    </div>
</template>

<script setup>
import gsap from "gsap"
import { onMounted, ref } from 'vue'
const testGsap = ref(null)
onMounted(() => {
  gsap.set(testGsap.value, { transform: 'translateX(-100px)', color: 'blue' })
  gsap.to(testGsap.value, { transform: 'translateX(100px)', color: 'pink', duration: 1, delay: 0 })
})
</script>

20220902_180847

测试2 - 手动控制执行动画

<template>
    <div  class="w-screen h-[400px] flex items-center justify-center">
      <div ref="testGsap" class="border border-zinc-300 rounded-sm p-4">test GSAP</div>
    </div>
    <Button @click="handleStart">执行动画</Button>
    <Button @click="handleReverse">翻转动画</Button>
</template>

<script setup>
import gsap from "gsap"
import { onMounted, ref } from 'vue'
const testGsap = ref(null)
let tween
onMounted(() => {
  gsap.set(testGsap.value, { transform: 'translateX(-100px)', color: 'blue' })
  tween = gsap.to(testGsap.value, { transform: 'translateX(100px)', color: 'pink', duration: 1, delay: 0 })
  tween.pause();
})
const handleStart = () => {
   tween.play()
}
const handleReverse = () => {
   tween.reverse()
}
</script>

也就是当我们不主动暂停的话, gsap.to函数调用之后就会开始执行动画

34.5、实现从首页调到详情页
  • 1、创建pins/components/pins.vue组件

    image-20220903100744179

  • 2、在首页中使用Pins组件,并使用translation包裹、并设置执行动画

    <transition
        :css="false"
        @before-enter="onBeforeEnter"
        @enter="onEnter"
        @after-enter="onAfterEnter"
        @leave="onLeave"
        @after-leave="onAfterLeave"
      >
        <Pins :id="currentItem.id" v-if="pinsVisible"/>
      </transition>
    
    
  • 3、点击每一项时,计算当前项距离屏幕左边和边的距离、并利用h5的pushState改变地址栏路径

  • 4、展示Pins组件, 在展示过程中在过渡钩子函数中设置对应的动画样式

  • 5、当需要关闭Pins组件时; 我们需要监听页面的回退事件popState,当时间被调用时关闭Pins组件

先看下我们要实现的效果

20220903_102106

开始实现

list/index.js

<template>
  <div class="w-full">
    ...
  <!-- 图片详情 -->
  <transition
    :css="false"
    @before-enter="onBeforeEnter"
    @enter="onEnter"
    @after-enter="onAfterEnter"
    @leave="onLeave"
    @after-leave="onAfterLeave"
  >
    <Pins :id="currentItem.id" v-if="pinsVisible"/>
  </transition>
</template>

<script setup>
import ListItem from './item/index.vue'
import { getPexels } from '@/api/pexels'
import { isMoboleTerminal } from '@/utils/flexible'
import { ref, watch, computed } from 'vue'
import { useStore } from 'vuex'
import Pins from '@/views/pins/components/pins.vue'
import gsap from 'gsap'
import { useEventListener } from '@vueuse/core'

const store = useStore()

// 选中item
const selectItem = (item) => {
  currentItem.value = item
  // 修改页面地址
  window.history.pushState(null, '', '/pins/' + item.id)
}
// 监听页面回退
useEventListener('popstate', () => {
  delete currentItem.value.id
})
const pinsVisible = computed(() => currentItem.value.id !== void 0)
// pins动画钩子 -- 动画执行之前
const onBeforeEnter = (el) => {
  gsap.set(el, {
    scaleX: 0.2,
    scaleY: 0.2,
    transformOrigin: '0 0',
    translateX: currentItem.value.translateX,
    translateY: currentItem.value.translateY,
    opacity: 0
  })
}

// pins动画钩子 -- 动画执行过程
const onEnter = (el, done) => {
  el.__gsap__ = gsap.to(el, {
    duration: 0.4,
    scaleX: 1,
    scaleY: 1,
    transformOrigin: '0 0',
    translateX: 0,
    translateY: 0,
    opacity: 1,
    onComplete: done
  })
}

// pins动画钩子 -- 动画离开过程
const onLeave = (el, done) => {
  el.__gsap__.reverse()
  setTimeout(() => {
    done()
  }, el.__gsap__._dur * 1500)
}

const onAfterLeave = (el) => {
  currentItem.value = {}
}
</script>

item.vue

const handleSelectItem = () => {
   
  // 获取图片中间路基浏览器左边和顶部的距离
  const {
    left, top, width, height }  = imgEle.value?.getBoundingClientRect()
  const translateX = left + width / 2
  const translateY = top + height / 2
  emits('selectItem', {
   
    ...props.pexel,
    translateX,
    translateY
  })
}
34.5、解决刷新丢失的问题 - 路由props传参

所谓的刷新丢失,就是刷新之后、会直接访问我们设置的路径、而路径没有没有匹配到对应的路由组件、所以就会显示空白页面;

所以,我们的思路是:

方案1:

  • 1、在路由表中配置对应连接的路由对象
  • 2、路由对象中的组件中使用到我们上面定义的pins.vue组件
  • 3、这样刷新时就会通过路由匹配到对应的路由组件,在路由初始化时获取id参数传递给组件

方案2:路由props传参

vue-router 中 props传参给组件

在你的组件中使用 $route 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为:

我们可以将下面的代码

const User = {
   
  template: '<div>User {
   { $route.params.id }}</div>'
}
const routes = [{
    path: '/user/:id', component: User }]

替换成

const User = {
   
  // 请确保添加一个与路由参数完全相同的 prop 名
  props: ['id'],
  template: '<div>User {
   { id }}</div>'
}
const routes = [{
    path: '/user/:id', component: User, props: true }]

这允许你在任何地方使用该组件,使得该组件更容易重用和测试。

本案例中我们使用路由props传参

export default [
  {
   
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spider Cat 蜘蛛猫

你的鼓励将会是我最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值