当前位置:   article > 正文

vue3+threejs新手从零开发卡牌游戏(九):添加抽卡逻辑和动效

vue3+threejs新手从零开发卡牌游戏(九):添加抽卡逻辑和动效

首先优化下之前的代码,把game/deck/p1.vue中修改卡组方法和渲染卡组文字方法提到公共方法中,此时utils/common.ts完整代码如下:

  1. import { nextTick } from 'vue';
  2. import * as THREE from 'three';
  3. import * as TWEEN from '@tweenjs/tween.js'
  4. import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
  5. // 坐标归一化
  6. const transPos = (x: any, y: any) => {
  7. let pointer = new THREE.Vector2()
  8. pointer.x = ( x / window.innerWidth ) * 2 - 1;
  9. pointer.y = - ( y / window.innerHeight ) * 2 + 1;
  10. return pointer
  11. }
  12. export { transPos }
  13. // 修改卡组
  14. const editDeckCard = (group:any, mesh: any, type: any) => {
  15. return new Promise((resolve, reject) => {
  16. let text = group.children.find((v: any) => v.name === "卡组数量")
  17. let shadowText = group.children.find((v: any) => v.name === "卡组数量阴影")
  18. // console.log(22, group.children, commonStore.$state.p1Deck)
  19. if (type === "remove") { // 删除卡组中的卡牌
  20. let child = group.children.find((v: any) => v.name === mesh.name)
  21. if (child) {
  22. group.remove(child)
  23. }
  24. }
  25. group.remove(text)
  26. group.remove(shadowText)
  27. group.children.forEach((v: any, i: any) => {
  28. v.position.set(0, 0.005 * i, 0)
  29. })
  30. resolve(true)
  31. })
  32. }
  33. export { editDeckCard }
  34. // 渲染卡组文字
  35. const renderText = (group: any, text: any, font: any, position: any) => {
  36. return new Promise((resolve, reject) => {
  37. const geometry = new TextGeometry( `${text}`, {
  38. font,
  39. size: 0.4,
  40. height: 0,
  41. curveSegments: 4,
  42. bevelEnabled: true,
  43. bevelThickness: 0,
  44. bevelSize: 0,
  45. bevelSegments: 0
  46. });
  47. geometry.center()
  48. const material = new THREE.MeshBasicMaterial({
  49. color: new THREE.Color("white"),
  50. alphaHash: true
  51. })
  52. const mesh = new THREE.Mesh( geometry, material ) ;
  53. mesh.position.set(position.x, position.y + 0.01, position.z)
  54. // mesh.position.set(0, 0.005 * commonStore.$state.p1Deck.length + 0.01, 0)
  55. mesh.rotateX(-90 * (Math.PI / 180)) // 弧度
  56. mesh.name = "卡组数量"
  57. // 阴影
  58. let shadowGeometry = geometry.clone()
  59. shadowGeometry.translate(0.02, 0.02, 0);
  60. let shadowMaterial = new THREE.MeshBasicMaterial({
  61. color: new THREE.Color("black"),
  62. alphaHash: true
  63. });
  64. let shadowMesh = new THREE.Mesh(shadowGeometry, shadowMaterial);
  65. shadowMesh.position.set(position.x, position.y, position.z)
  66. // shadowMesh.position.set(0, 0.005 * commonStore.$state.p1Deck.length, 0)
  67. shadowMesh.rotateX(-90 * (Math.PI / 180)) // 弧度
  68. shadowMesh.name = "卡组数量阴影"
  69. group.add(mesh)
  70. group.add(shadowMesh)
  71. resolve(true)
  72. })
  73. }
  74. export { renderText }

同步修改game/index.vue中对修改卡组方法的使用:

  1. import { transPos, editDeckCard, renderText } from "@/utils/common.ts"
  2. // 初始化手牌
  3. const initHand = () => {
  4. let cardNumber = 4
  5. let _number = 0
  6. let p1Deck = JSON.parse(JSON.stringify(commonStore.$state.p1Deck))
  7. let deckGroup = scene.getObjectByName("p1_deckGroup")
  8. let position = new THREE.Vector3(0, 0.005 * p1Deck.length, 0)
  9. let _interval = setInterval(async() => {
  10. // console.log(123, p1Deck)
  11. if (_number < cardNumber) {
  12. let obj = p1Deck[p1Deck.length - 1]
  13. p1Deck.splice(p1Deck.length-1, 1)
  14. commonStore.updateP1Deck(p1Deck)
  15. // 修改卡组
  16. await editDeckCard(deckGroup, obj, "remove")
  17. await renderText(deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)
  18. // 手牌区添加手牌
  19. handRef.value.addHandCard(obj, deckGroup)
  20. } else {
  21. clearInterval(_interval)
  22. }
  23. _number++
  24. }, 200)
  25. }

之后我们思考下抽卡逻辑:
1.点击卡组,将卡组平移到页面中心位置(0, 0, 0)(y轴可以适当调高一点,这里点击事件只是为了测试抽卡,之后可以删掉)
2.然后卡组上展示“点击抽卡”的文字(文字用的是div+css的形式)

3.点击抽卡,执行抽卡动画

4.抽卡结束后,卡组移回到原位

首先我们在game/index.vue中添加点击事件,用raycaster射线进行鼠标拾取,当点击在卡组上时,执行onP1DeckEvent,这里由于是移动端所以只监听了touch事件,pc端则需要额外监听click事件:

  1. // 鼠标按下
  2. window.addEventListener('touchstart', onMousedown)
  3. // 鼠标按下事件
  4. const onMousedown = (ev: any) => {
  5. // console.log(222, ev.target)
  6. // 判断是否点击到canvas上
  7. if(!(ev.target instanceof HTMLCanvasElement)){
  8. return;
  9. }
  10. // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  11. let clientX = ev.clientX || ev.changedTouches[0].pageX
  12. let clientY = ev.clientY || ev.changedTouches[0].pageY
  13. // pointer.x = ( clientX / window.innerWidth ) * 2 - 1;
  14. // pointer.y = - ( clientY / window.innerHeight ) * 2 + 1;
  15. let point = transPos(clientX, clientY) // 卡组起始位置的屏幕坐标
  16. // 通过摄像机和鼠标位置更新射线
  17. raycaster.setFromCamera( point, camera );
  18. // 点击卡组事件
  19. onP1DeckEvent()
  20. }
  21. // 点击卡组事件
  22. const onP1DeckEvent = () => {
  23. if (commonStore.$state.p1Deck.length <= 0) {
  24. return
  25. }
  26. let p1_deckGroup = scene.getObjectByName("p1_deckGroup")
  27. let arr = raycaster.intersectObject(p1_deckGroup, true)
  28. if (arr.length <= 0) {
  29. return
  30. }
  31. let pos1 = p1_deckGroup.userData.position
  32. let pos2 = new THREE.Vector3(0, 2, 0)
  33. // console.log(444, pos1, pos2)
  34. if (p1_deckGroup.position.x !== pos2.x) {
  35. drawCardRef.value.drawCardAnimate1(p1_deckGroup, pos1, pos2)
  36. }
  37. }

然后我们在src下新建一个抽卡组件:

这个组件其实是用div+css画了一个透明的蒙层,卡组移到中央后,展示蒙层和“点击抽卡”字样,然后卡组动画我们分为两步,第一步是卡组飞到页面中心,用户点击抽卡后,执行之前的修改卡组和添加手牌方法,然后隐藏蒙层,执行第二步卡组移回原文动画,components/DrawCard.vue完整代码如下:

  1. <!-- 抽卡 -->
  2. <template>
  3. <div v-if="state.visible" ref="maskRef" class="mask">
  4. <div class="box" @click="onDrawCard">
  5. <p>点击抽卡</p>
  6. </div>
  7. </div>
  8. </template>
  9. <script setup lang="ts">
  10. import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
  11. import { useCommonStore } from "@/stores/common.ts"
  12. import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
  13. import { transPos, editDeckCard, renderText } from "@/utils/common.ts"
  14. const props: any = defineProps({
  15. handRef: {},
  16. deckRef: {}
  17. })
  18. // 引入threejs变量
  19. const {proxy} = getCurrentInstance()
  20. const THREE = proxy['THREE']
  21. const scene = proxy['scene']
  22. const camera = proxy['camera']
  23. const renderer = proxy['renderer']
  24. const TWEEN = proxy['TWEEN']
  25. const commonStore = useCommonStore()
  26. const maskRef = ref()
  27. const state = reactive({
  28. visible: false
  29. })
  30. // 卡组抽卡动画1-卡组移到页面中央
  31. const drawCardAnimate1 = (deckGroup: any, startPos: any, endPos: any) => {
  32. // 隐藏卡组数量和卡组数量阴影几何体
  33. let textMesh = deckGroup.children.find((v: any) => v.name === "卡组数量")
  34. let textShadowMesh = deckGroup.children.find((v: any) => v.name === "卡组数量阴影")
  35. const tw = new TWEEN.Tween({
  36. x: startPos.x,
  37. y: startPos.y,
  38. z: startPos.z,
  39. opacity: 1.0,
  40. group: deckGroup
  41. })
  42. tw.to({
  43. x: endPos.x,
  44. y: endPos.y,
  45. z: endPos.z,
  46. opacity: 0.0,
  47. }, 200)
  48. tw.easing(TWEEN.Easing.Quadratic.Out)
  49. tw.onUpdate((obj: any) => {
  50. obj.group.position.set(obj.x, obj.y, obj.z)
  51. textMesh.material.opacity = obj.opacity
  52. textShadowMesh.material.opacity = obj.opacity
  53. })
  54. tw.onComplete(function() {
  55. state.visible = true
  56. TWEEN.remove(tw)
  57. })
  58. tw.start();
  59. }
  60. // 卡组抽卡动画2-卡组回到原位
  61. const drawCardAnimate2 = (deckGroup: any, startPos: any, endPos: any) => {
  62. // 隐藏卡组数量和卡组数量阴影几何体
  63. let textMesh = deckGroup.children.find((v: any) => v.name === "卡组数量")
  64. let textShadowMesh = deckGroup.children.find((v: any) => v.name === "卡组数量阴影")
  65. // textMesh.material.opacity = 1
  66. // textShadowMesh.material.opacity = 1
  67. const tw = new TWEEN.Tween({
  68. x: startPos.x,
  69. y: startPos.y,
  70. z: startPos.z,
  71. opacity: 0,
  72. group: deckGroup
  73. })
  74. tw.to({
  75. x: endPos.x,
  76. y: endPos.y,
  77. z: endPos.z,
  78. opacity: 1,
  79. }, 200)
  80. tw.easing(TWEEN.Easing.Quadratic.Out)
  81. tw.onUpdate((obj: any) => {
  82. obj.group.position.set(obj.x, obj.y, obj.z)
  83. textMesh.material.opacity = obj.opacity
  84. textShadowMesh.material.opacity = obj.opacity
  85. })
  86. tw.onComplete(function() {
  87. TWEEN.remove(tw)
  88. })
  89. tw.start();
  90. }
  91. // 抽卡
  92. const onDrawCard = async () => {
  93. let p1_deckGroup = scene.getObjectByName("p1_deckGroup")
  94. let p1Deck = JSON.parse(JSON.stringify(commonStore.$state.p1Deck))
  95. let position = new THREE.Vector3(0, 0.005 * p1Deck.length, 0)
  96. let obj = p1Deck[p1Deck.length - 1]
  97. p1Deck.splice(p1Deck.length-1, 1)
  98. commonStore.updateP1Deck(p1Deck)
  99. // 修改卡组
  100. await editDeckCard(p1_deckGroup, obj, "remove")
  101. await renderText(p1_deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)
  102. // 手牌区添加手牌
  103. props.handRef.addHandCard(obj, p1_deckGroup)
  104. state.visible = false
  105. setTimeout(() => {
  106. nextTick(() => {
  107. drawCardAnimate2(p1_deckGroup, p1_deckGroup.position, p1_deckGroup.userData.position)
  108. })
  109. }, 500)
  110. }
  111. defineExpose({
  112. drawCardAnimate1,
  113. drawCardAnimate2
  114. })
  115. </script>
  116. <style lang="scss" scoped>
  117. .mask {
  118. position: fixed;
  119. top: 0;
  120. left: 0;
  121. z-index: 10;
  122. display: flex;
  123. align-items: center;
  124. justify-content: center;
  125. width: 100%;
  126. height: 100vh;
  127. // background-color: white;
  128. .box {
  129. width: 50vh;
  130. // height: 100px;
  131. text-align: center;
  132. font-size: 20px;
  133. color: #fff;
  134. }
  135. }
  136. </style>

注意:由于点击事件会穿透蒙层,所以我们在onMousedown方法中添加了ev.target instanceof HTMLCanvasElement进行判断是否点击到了canvas上。

最后还优化了下卡组,给卡组添加了线框,game/deck/p1.vue完整代码如下:

  1. <template>
  2. <div></div>
  3. </template>
  4. <script setup lang="ts">
  5. import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
  6. import { useCommonStore } from "@/stores/common.ts"
  7. import { TextGeometry } from 'three/addons/geometries/TextGeometry.js';
  8. import { Card } from "@/views/game/Card.ts"
  9. import { CARD_DICT } from "@/utils/dict/card.ts"
  10. import { transPos, renderText } from "@/utils/common.ts"
  11. // 引入threejs变量
  12. const {proxy} = getCurrentInstance()
  13. const THREE = proxy['THREE']
  14. const scene = proxy['scene']
  15. const camera = proxy['camera']
  16. const renderer = proxy['renderer']
  17. const TWEEN = proxy['TWEEN']
  18. const raycaster = new THREE.Raycaster();
  19. const pointer = new THREE.Vector2();
  20. const commonStore = useCommonStore()
  21. // 卡组group
  22. const deckGroup = new THREE.Group()
  23. deckGroup.name = "p1_deckGroup"
  24. scene.add(deckGroup)
  25. const init = () => {
  26. setDeckPos()
  27. addDeckWireframe()
  28. commonStore.$state.p1Deck.forEach((v: any, i: any) => {
  29. let obj = CARD_DICT.find((b: any) => b.card_id === v.card_id)
  30. if (obj) {
  31. let card = new Card(obj)
  32. let mesh = card.init()
  33. mesh.position.set(0, 0.005 * i, 0)
  34. mesh.rotateX(180 * (Math.PI / 180)) // 弧度
  35. mesh.name = v.name
  36. deckGroup.add( mesh );
  37. }
  38. })
  39. let position = new THREE.Vector3(0, 0.005 * commonStore.$state.p1Deck.length, 0)
  40. renderText(deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)
  41. }
  42. // 设置卡组位置
  43. const setDeckPos = () => {
  44. nextTick(() => {
  45. let plane = scene.getObjectByName("地面")
  46. let point = transPos(window.innerWidth - 15, window.innerHeight - 15) // 卡组起始位置的屏幕坐标
  47. //
  48. raycaster.setFromCamera( point, camera );
  49. const intersects1 = raycaster.intersectObject( plane );
  50. if (intersects1.length > 0) {
  51. let point = intersects1[0].point
  52. // deckGroup.position.set(point.x, point.y, point.z)
  53. deckGroup.position.set(point.x - 0.5, point.y, point.z - 0.7)
  54. // 记录卡组位置
  55. let position = new THREE.Vector3(point.x - 0.5, point.y, point.z - 0.7)
  56. deckGroup.userData["position"] = position
  57. }
  58. })
  59. }
  60. // 绘制卡组区域线框
  61. const addDeckWireframe = () => {
  62. nextTick(() => {
  63. const edges = new THREE.EdgesGeometry( deckGroup.children[0].geometry );
  64. const line = new THREE.LineSegments( edges, new THREE.LineBasicMaterial( { color: 0xffffff } ) );
  65. deckGroup.add(line);
  66. })
  67. }
  68. defineExpose({
  69. init,
  70. })
  71. </script>
  72. <style lang="scss" scoped>
  73. </style>

最后看下抽卡效果:

至此基本完成了抽卡逻辑和简单动效的开发。

附录:

game/index.vue完整代码如下:

  1. <template>
  2. <div ref="sceneRef" class="scene"></div>
  3. <!-- 手牌 -->
  4. <Hand ref="handRef"/>
  5. <!-- 卡组 -->
  6. <Deck ref="deckRef"/>
  7. <!-- 抽卡逻辑 -->
  8. <DrawCard ref="drawCardRef" :handRef="handRef" :deckRef="deckRef"/>
  9. </template>
  10. <script setup lang="ts">
  11. import { reactive, ref, onMounted, onBeforeUnmount, watch, defineComponent, getCurrentInstance, nextTick } from 'vue'
  12. import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // 轨道控制器
  13. import { FontLoader } from 'three/addons/loaders/FontLoader.js';
  14. import { useCommonStore } from "@/stores/common.ts"
  15. import { transPos, editDeckCard, renderText } from "@/utils/common.ts"
  16. import { Card } from "./Card.ts"
  17. import { CARD_DICT } from "@/utils/dict/card.ts"
  18. import Hand from "./hand/index.vue"
  19. import Deck from "./deck/index.vue"
  20. import DrawCard from "@/components/DrawCard.vue"
  21. // 引入threejs变量
  22. const {proxy} = getCurrentInstance()
  23. const THREE = proxy['THREE']
  24. const scene = proxy['scene']
  25. const camera = proxy['camera']
  26. const renderer = proxy['renderer']
  27. const TWEEN = proxy['TWEEN']
  28. const raycaster = new THREE.Raycaster();
  29. const pointer = new THREE.Vector2();
  30. const commonStore = useCommonStore()
  31. // 场景ref
  32. const sceneRef = ref()
  33. const handRef = ref()
  34. const deckRef = ref()
  35. const drawCardRef = ref()
  36. // 坐标轴辅助
  37. const axesHelper = new THREE.AxesHelper(5);
  38. // 创建轨道控制器
  39. // const controls = new OrbitControls( camera, renderer.domElement );
  40. // 字体加载器
  41. const fontLoader = new FontLoader();
  42. onMounted(async () => {
  43. await initResource()
  44. initScene()
  45. initGame()
  46. // 鼠标按下
  47. window.addEventListener('touchstart', onMousedown)
  48. // window.addEventListener('click', onMousedown)
  49. // 监听浏览器窗口变化进行场景自适应
  50. window.addEventListener('resize', onWindowResize, false);
  51. })
  52. // 资源加载
  53. const initResource = () => {
  54. // 字体加载
  55. return new Promise((resolve, reject) => {
  56. fontLoader.load('fonts/helvetiker_regular.typeface.json', (font: any) => {
  57. commonStore.loadFont(font)
  58. resolve(true)
  59. });
  60. })
  61. }
  62. // 初始化场景
  63. const initScene = () => {
  64. renderer.setSize( window.innerWidth, window.innerHeight );
  65. sceneRef.value.appendChild( renderer.domElement );
  66. scene.add(axesHelper)
  67. // camera.position.set( 5, 5, 5 );
  68. camera.position.set( 0, 6.5, 0 );
  69. camera.lookAt(0, 0, 0)
  70. addPlane()
  71. animate();
  72. }
  73. // scene中添加plane几何体
  74. const addPlane = () => {
  75. const geometry = new THREE.PlaneGeometry( 20, 20);
  76. const material = new THREE.MeshBasicMaterial( {
  77. color: new THREE.Color("gray"),
  78. side: THREE.FrontSide,
  79. alphaHash: true,
  80. // alphaTest: 0,
  81. opacity: 0
  82. } );
  83. const plane = new THREE.Mesh( geometry, material );
  84. plane.rotateX(-90 * (Math.PI / 180)) // 弧度
  85. plane.name = "地面"
  86. scene.add( plane );
  87. }
  88. // 用requestAnimationFrame进行渲染循环
  89. const animate = () => {
  90. requestAnimationFrame( animate );
  91. TWEEN.update()
  92. renderer.render( scene, camera );
  93. }
  94. // 场景跟随浏览器窗口大小自适应
  95. const onWindowResize = () => {
  96. camera.aspect = window.innerWidth / window.innerHeight;
  97. camera.updateProjectionMatrix();
  98. renderer.setSize(window.innerWidth, window.innerHeight);
  99. }
  100. // 初始化游戏
  101. const initGame = async () => {
  102. // 初始化卡组
  103. await initDeck()
  104. // 初始化手牌
  105. initHand()
  106. }
  107. // 初始化卡组
  108. const initDeck = () => {
  109. return new Promise((resolve, reject) => {
  110. let p1Deck = [
  111. "YZ-01",
  112. "YZ-02",
  113. "YZ-03",
  114. "YZ-04",
  115. "YZ-01",
  116. "YZ-02",
  117. // "YZ-03",
  118. // "YZ-04",
  119. // "YZ-01",
  120. // "YZ-02",
  121. // "YZ-03",
  122. // "YZ-04",
  123. ]
  124. // 洗牌
  125. p1Deck.sort(() => {
  126. return Math.random() - 0.5
  127. })
  128. let newDeck: any = []
  129. p1Deck.forEach((v: any, i: any) => {
  130. let obj = CARD_DICT.find((b: any) => b.card_id === v)
  131. if (obj) {
  132. newDeck.push({
  133. card_id: v,
  134. name: `${obj.name}_${i}`
  135. })
  136. }
  137. })
  138. // console.log("p1Deck", newDeck)
  139. commonStore.updateP1Deck(newDeck)
  140. nextTick(() => {
  141. handRef.value.init()
  142. deckRef.value.init()
  143. resolve(true)
  144. })
  145. })
  146. }
  147. // 初始化手牌
  148. const initHand = () => {
  149. let cardNumber = 4
  150. let _number = 0
  151. let p1Deck = JSON.parse(JSON.stringify(commonStore.$state.p1Deck))
  152. let deckGroup = scene.getObjectByName("p1_deckGroup")
  153. let position = new THREE.Vector3(0, 0.005 * p1Deck.length, 0)
  154. let _interval = setInterval(async() => {
  155. // console.log(123, p1Deck)
  156. if (_number < cardNumber) {
  157. let obj = p1Deck[p1Deck.length - 1]
  158. p1Deck.splice(p1Deck.length-1, 1)
  159. commonStore.updateP1Deck(p1Deck)
  160. // 修改卡组
  161. await editDeckCard(deckGroup, obj, "remove")
  162. await renderText(deckGroup, `${commonStore.$state.p1Deck.length}`, commonStore.$state._font, position)
  163. // 手牌区添加手牌
  164. handRef.value.addHandCard(obj, deckGroup)
  165. } else {
  166. clearInterval(_interval)
  167. }
  168. _number++
  169. }, 200)
  170. }
  171. // 鼠标按下事件
  172. const onMousedown = (ev: any) => {
  173. // console.log(222, ev.target)
  174. // 判断是否点击到canvas上
  175. if(!(ev.target instanceof HTMLCanvasElement)){
  176. return;
  177. }
  178. // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  179. let clientX = ev.clientX || ev.changedTouches[0].pageX
  180. let clientY = ev.clientY || ev.changedTouches[0].pageY
  181. // pointer.x = ( clientX / window.innerWidth ) * 2 - 1;
  182. // pointer.y = - ( clientY / window.innerHeight ) * 2 + 1;
  183. let point = transPos(clientX, clientY) // 卡组起始位置的屏幕坐标
  184. // 通过摄像机和鼠标位置更新射线
  185. raycaster.setFromCamera( point, camera );
  186. // 点击卡组事件
  187. onP1DeckEvent()
  188. }
  189. // 点击卡组事件
  190. const onP1DeckEvent = () => {
  191. if (commonStore.$state.p1Deck.length <= 0) {
  192. return
  193. }
  194. let p1_deckGroup = scene.getObjectByName("p1_deckGroup")
  195. let arr = raycaster.intersectObject(p1_deckGroup, true)
  196. if (arr.length <= 0) {
  197. return
  198. }
  199. let pos1 = p1_deckGroup.userData.position
  200. let pos2 = new THREE.Vector3(0, 2, 0)
  201. // console.log(444, pos1, pos2)
  202. if (p1_deckGroup.position.x !== pos2.x) {
  203. drawCardRef.value.drawCardAnimate1(p1_deckGroup, pos1, pos2)
  204. }
  205. }
  206. </script>
  207. <style lang="scss" scoped>
  208. .scene {
  209. position: fixed;
  210. top: 0;
  211. left: 0;
  212. width: 100%;
  213. height: 100vh;
  214. }
  215. </style>

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号