33、实现全屏展示功能
我们知道在原生dom上,提供了一些方法来供我们开启或关闭全屏:
Element.requestFullscreen()Document.exitFullscreen()Document.fullscreenDocument.fullscreenElement
一般浏览器
使用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、需求分析
首先我们看一下首页的图片

分析:
- 当点击某一个图片时、跳转到对应图片的详情页
 - 并且在跳转的过程中有从小到放大的动画的效果(类似于全屏效果的动画)
 
34.2、分析现阶段路由跳转动画
在vue-router页面跳转如果要实现跳转到动画,需要借助于transition组件来进行实现动画
<router-view v-slot="{ Component, route }">
  <transition name="fade">
    <component :is="Component" />
  </transition>
</router-view>
 

这是在vue官网截的图,从图中我们可以得知transition组件一般适用于 组件 或 元素的显示和隐藏、并不适合我们的需求、
34.3、提出解决方案
那么根据咱们上一小节的分析,我们知道通过 vue-router 的过渡动效是无法实现咱们期望的路由切换效果的。
那么我们应该如何去做呢?
想要搞明白咱们的可行性方案,那么首先我们得先来搞清楚什么是路由的跳转?
所谓路由的跳转无非指的是两个部分:
-  
1.浏览器的url 发生了变化
 -  
2.浏览器中展示的页面组件发生了变化
 
那么只要满足这两点,我们就认为路径进行了跳转
所以说,我们是不是可以换个思路,我们不去进行真实的路由跳转,而是先修改浏览器的URL,再切换展示的页面(以组件的形式覆盖整个浏览器可视区域)。这样对于用户而言,是不是就完成了整个的路由跳转工作。
所以说我们的具体问题就变成了:
- 1.如何让浏览器的url发生变化,但是不跳转页面
 - 2.如何让一个新的组件以包含动画的形式进行展示 
  
- 那么想要完成第一个功能我们可以使用:
History.pushState()方法 - 而第二个功能我们可以使用 
GSAP这个动画库来进行实现。 
 - 那么想要完成第一个功能我们可以使用:
 
34.4、关于GSAP介绍
GSAP, 它是一个非常强大的js动画库, 他支持Flip、滚动动画等;在其内部给我们提供了非常多的方法供我们来使用;
本次我们使用到的api,只有set和to两个:
-  
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>
 

测试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组件

 -  
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组件 
先看下我们要实现的效果

开始实现
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 [
  {
   
                


















