Commit 81471c00 authored by 董政锦's avatar 董政锦

feat: game4 本地无法测试;

parent e89882f9
This diff is collapsed.
<svg width="338" height="119" viewBox="0 0 338 119" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7199 17.3104C20.5429 12.5095 24.7052 9 29.5761 9H322.614C328.7 9 333.375 14.3899 332.514 20.4142L320.371 105.414C319.667 110.341 315.448 114 310.472 114H15.0047C8.80654 114 4.10121 108.419 5.14847 102.31L19.7199 17.3104Z" fill="#DDA706"/>
<path d="M322.614 6.5C330.221 6.5 336.065 13.2373 334.989 20.7676L322.846 105.768C321.966 111.926 316.692 116.5 310.472 116.5H15.0049C7.25719 116.5 1.37549 109.524 2.68457 101.888L17.2559 16.8877C18.2847 10.8867 23.4876 6.5 29.5762 6.5H322.614Z" stroke="#A27900" stroke-opacity="0.6" stroke-width="5"/>
<path d="M21.7199 8.31036C22.5429 3.5095 26.7052 0 31.5761 0H322.83C328.84 0 333.493 5.26398 332.754 11.2287L322.23 96.2287C321.61 101.238 317.354 105 312.306 105H17.0047C10.8065 105 6.10121 99.4194 7.14847 93.3104L21.7199 8.31036Z" fill="#FFCE3C"/>
<path d="M22.571 8.58873C23.4857 3.89102 27.6007 0.5 32.3866 0.5H322.991C328.944 0.5 333.579 5.6693 332.932 11.5873L328.058 56.1507C327.53 60.9784 323.609 64.7288 318.763 65.0425L173.514 74.4437C172.936 74.4812 172.355 74.4682 171.778 74.4049L23.9465 58.1852C18.1314 57.5472 14.1034 52.0757 15.2215 46.3336L22.571 8.58873Z" fill="white" fill-opacity="0.3"/>
<path d="M85.0725 69.576C89.2165 66.048 92.8565 62.464 95.8805 58.824L88.3765 48.072H86.3045V49.304C86.3045 56.584 85.1845 62.912 82.3285 69.576H74.0965C78.1845 62.8 79.3045 57.032 79.3045 49.304V29.928C89.7765 29.928 100.361 29.536 110.721 28.752V35.472C102.657 36.144 94.5365 36.48 86.3045 36.592V41.744H112.345C110.329 47.904 107.585 53.448 104.113 58.432L112.345 69.576H104.449L100.081 63.696C98.2885 65.768 96.3845 67.728 94.3125 69.576H85.0725ZM60.8245 52.44V45.16H74.7125C74.7125 54.456 74.1525 61.68 72.4725 69.464C75.8325 71.312 79.6965 72.04 85.4645 72.04H112.905V79.04H85.4645C78.6325 79.04 74.3765 78.312 70.7365 76.464C70.4005 77.472 70.1205 78.536 69.7845 79.6H62.0565C65.6405 69.632 67.0405 61.904 67.4325 52.44H60.8245ZM63.1205 29.2H70.6805L74.5445 41.352H66.9845L63.1205 29.2ZM96.3285 48.072L99.9125 53.336C101.089 51.6 102.097 49.864 102.993 48.072H96.3285ZM119.961 30.768H166.329V78.032H119.961V30.768ZM127.185 70.864H159.105V37.936H127.185V70.864ZM131.665 66.496V42.304H154.625V66.496H131.665ZM138.777 59.384H147.513V49.36H138.777V59.384ZM221.321 41.744V79.208H176.969V41.744H194.777L195.393 38.944H173.217V32.504H184.529L183.129 28.528H190.745L192.145 32.504H206.145L207.545 28.528H215.161L213.761 32.504H225.073V38.944H202.785L202.169 41.744H221.321ZM184.249 72.936H214.041V68.736H184.249V72.936ZM184.249 62.576H214.041V58.488H184.249V62.576ZM184.249 52.272H214.041V48.072H184.249V52.272ZM233.697 66.664V41.072H251.449V36.816H229.945V29.704H280.345V36.816H258.841V41.072H276.593V66.664H269.369V48.24H240.921V66.664H233.697ZM229.889 80.16V72.88C243.721 69.744 252.009 63.976 252.009 51.04H259.177C259.177 66.776 249.993 76.016 229.889 80.16ZM256.545 72.32V65.04L280.345 72.88V80.16L256.545 72.32Z" fill="white"/>
<path d="M83.0725 66.576C87.2165 63.048 90.8565 59.464 93.8805 55.824L86.3765 45.072H84.3045V46.304C84.3045 53.584 83.1845 59.912 80.3285 66.576H72.0965C76.1845 59.8 77.3045 54.032 77.3045 46.304V26.928C87.7765 26.928 98.3605 26.536 108.721 25.752V32.472C100.657 33.144 92.5365 33.48 84.3045 33.592V38.744H110.345C108.329 44.904 105.585 50.448 102.113 55.432L110.345 66.576H102.449L98.0805 60.696C96.2885 62.768 94.3845 64.728 92.3125 66.576H83.0725ZM58.8245 49.44V42.16H72.7125C72.7125 51.456 72.1525 58.68 70.4725 66.464C73.8325 68.312 77.6965 69.04 83.4645 69.04H110.905V76.04H83.4645C76.6325 76.04 72.3765 75.312 68.7365 73.464C68.4005 74.472 68.1205 75.536 67.7845 76.6H60.0565C63.6405 66.632 65.0405 58.904 65.4325 49.44H58.8245ZM61.1205 26.2H68.6805L72.5445 38.352H64.9845L61.1205 26.2ZM94.3285 45.072L97.9125 50.336C99.0885 48.6 100.097 46.864 100.993 45.072H94.3285ZM117.961 27.768H164.329V75.032H117.961V27.768ZM125.185 67.864H157.105V34.936H125.185V67.864ZM129.665 63.496V39.304H152.625V63.496H129.665ZM136.777 56.384H145.513V46.36H136.777V56.384ZM219.321 38.744V76.208H174.969V38.744H192.777L193.393 35.944H171.217V29.504H182.529L181.129 25.528H188.745L190.145 29.504H204.145L205.545 25.528H213.161L211.761 29.504H223.073V35.944H200.785L200.169 38.744H219.321ZM182.249 69.936H212.041V65.736H182.249V69.936ZM182.249 59.576H212.041V55.488H182.249V59.576ZM182.249 49.272H212.041V45.072H182.249V49.272ZM231.697 63.664V38.072H249.449V33.816H227.945V26.704H278.345V33.816H256.841V38.072H274.593V63.664H267.369V45.24H238.921V63.664H231.697ZM227.889 77.16V69.88C241.721 66.744 250.009 60.976 250.009 48.04H257.177C257.177 63.776 247.993 73.016 227.889 77.16ZM254.545 69.32V62.04L278.345 69.88V77.16L254.545 69.32Z" fill="#A00000"/>
</svg>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25 8C29.224 8 33.118 9.402 36.276 11.742L39.008 9.01C39.1937 8.82431 39.4141 8.67701 39.6567 8.57652C39.8994 8.47603 40.1594 8.4243 40.422 8.4243C40.6846 8.4243 40.9446 8.47603 41.1873 8.57652C41.4299 8.67701 41.6503 8.82431 41.836 9.01C42.0217 9.19569 42.169 9.41613 42.2695 9.65875C42.37 9.90136 42.4217 10.1614 42.4217 10.424C42.4217 10.6866 42.37 10.9466 42.2695 11.1893C42.169 11.4319 42.0217 11.6523 41.836 11.838L39.226 14.45C42.3 17.9079 43.9986 22.3733 44 27C44 37.476 35.476 46 25 46C14.524 46 6 37.476 6 27C6 16.524 14.524 8 25 8ZM25 12C16.728 12 10 18.728 10 27C10 35.272 16.728 42 25 42C33.272 42 40 35.272 40 27C40 18.728 33.272 12 25 12ZM24 18C24.2626 18 24.5227 18.0517 24.7654 18.1522C25.008 18.2528 25.2285 18.4001 25.4142 18.5858C25.5999 18.7715 25.7473 18.992 25.8478 19.2346C25.9483 19.4773 26 19.7374 26 20V26.93L31.11 30.336C31.5273 30.6403 31.8108 31.0941 31.9014 31.6026C31.9919 32.1111 31.8824 32.6349 31.5958 33.0645C31.3092 33.4942 30.8677 33.7964 30.3634 33.9081C29.8592 34.0199 29.3313 33.9324 28.89 33.664L22.89 29.664C22.6162 29.4813 22.3917 29.2338 22.2364 28.9436C22.0812 28.6533 22 28.3292 22 28V20C22 19.4696 22.2107 18.9609 22.5858 18.5858C22.9609 18.2107 23.4696 18 24 18ZM32 2C32.2627 1.99996 32.5228 2.05166 32.7655 2.15215C33.0082 2.25264 33.2287 2.39996 33.4144 2.58568C33.6002 2.7714 33.7475 2.99189 33.848 3.23456C33.9486 3.47723 34.0003 3.73733 34.0003 4C34.0003 4.26267 33.9486 4.52277 33.848 4.76544C33.7475 5.00811 33.6002 5.2286 33.4144 5.41432C33.2287 5.60004 33.0082 5.74736 32.7655 5.84785C32.5228 5.94834 32.2627 6.00004 32 6H18C17.7373 6.00004 17.4772 5.94834 17.2345 5.84785C16.9919 5.74736 16.7713 5.60004 16.5856 5.41432C16.3998 5.2286 16.2525 5.00811 16.152 4.76544C16.0514 4.52277 15.9997 4.26267 15.9997 4C15.9997 3.73733 16.0514 3.47723 16.152 3.23456C16.2525 2.99189 16.3998 2.7714 16.5856 2.58568C16.7713 2.39996 16.9919 2.25264 17.2345 2.15215C17.4772 2.05166 17.7373 1.99996 18 2H32Z" fill="white"/>
</svg>
<svg width="64" height="260" viewBox="0 0 64 260" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="33.5" y1="6.55671e-08" x2="33.5" y2="196" stroke="white" stroke-width="3"/>
<g clip-path="url(#clip0_152_314)">
<path d="M61.4862 215.545C59.8798 211.742 57.5505 208.287 54.6275 205.372C51.6595 202.396 48.1325 200.036 44.2493 198.428C40.366 196.819 36.2032 195.994 32 196C27.7968 195.994 23.6339 196.819 19.7507 198.428C15.8675 200.036 12.3405 202.396 9.37247 205.372C6.39627 208.34 4.0361 211.867 2.42764 215.751C0.819185 219.634 -0.00582988 223.797 3.10085e-05 228C-0.00582988 232.203 0.819185 236.366 2.42764 240.249C4.0361 244.133 6.39627 247.66 9.37247 250.628C12.3405 253.604 15.8675 255.964 19.7507 257.572C23.6339 259.181 27.7968 260.006 32 260C36.2032 260.006 40.366 259.181 44.2493 257.572C48.1325 255.964 51.6595 253.604 54.6275 250.628C57.6037 247.66 59.9639 244.133 61.5723 240.249C63.1808 236.366 64.0058 232.203 64 228C64.0084 223.721 63.1534 219.485 61.4862 215.545ZM32 255.463C16.8569 255.463 4.53691 243.143 4.53691 228C4.53691 212.857 16.8569 200.537 32 200.537C47.1431 200.537 59.4631 212.857 59.4631 228C59.4631 243.143 47.1431 255.463 32 255.463Z" fill="white"/>
<path d="M43.3129 216.686C42.5307 215.904 41.3431 215.826 40.6605 216.508L31.9991 225.17L23.3378 216.508C22.6552 215.826 21.4676 215.904 20.6854 216.686C19.9032 217.468 19.8249 218.656 20.5076 219.339L29.1689 228L20.5076 236.661C19.8249 237.344 19.9032 238.532 20.6854 239.314C21.4676 240.096 22.6552 240.174 23.3378 239.492L31.9991 230.827L40.6605 239.492C41.3431 240.174 42.5307 240.096 43.3129 239.314C44.0951 238.532 44.1734 237.344 43.4907 236.661L34.8258 228L43.4907 219.339C44.1734 218.656 44.0951 217.468 43.3129 216.686Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_152_314">
<rect width="64" height="64" fill="white" transform="translate(0 196)"/>
</clipPath>
</defs>
</svg>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
<svg width="338" height="119" viewBox="0 0 338 119" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7199 17.3104C20.5429 12.5095 24.7052 9 29.5761 9H322.614C328.7 9 333.375 14.3899 332.514 20.4142L320.371 105.414C319.667 110.341 315.448 114 310.472 114H15.0047C8.80654 114 4.10121 108.419 5.14847 102.31L19.7199 17.3104Z" fill="#DDA706"/>
<path d="M322.614 6.5C330.221 6.5 336.065 13.2373 334.989 20.7676L322.846 105.768C321.966 111.926 316.692 116.5 310.472 116.5H15.0049C7.25719 116.5 1.37549 109.524 2.68457 101.888L17.2559 16.8877C18.2847 10.8867 23.4876 6.5 29.5762 6.5H322.614Z" stroke="#A27900" stroke-opacity="0.6" stroke-width="5"/>
<path d="M21.7199 8.31036C22.5429 3.5095 26.7052 0 31.5761 0H322.83C328.84 0 333.493 5.26398 332.754 11.2287L322.23 96.2287C321.61 101.238 317.354 105 312.306 105H17.0047C10.8065 105 6.10121 99.4194 7.14847 93.3104L21.7199 8.31036Z" fill="#FF7700"/>
<path d="M22.571 8.58873C23.4857 3.89102 27.6007 0.5 32.3866 0.5H322.991C328.944 0.5 333.579 5.6693 332.932 11.5873L328.058 56.1507C327.53 60.9784 323.609 64.7288 318.763 65.0425L173.514 74.4437C172.936 74.4812 172.355 74.4682 171.778 74.4049L23.9465 58.1852C18.1314 57.5472 14.1034 52.0757 15.2215 46.3336L22.571 8.58873Z" fill="white" fill-opacity="0.3"/>
<path d="M94.3685 79.6V72.6H101.929V69.52H72.3605V79.6H65.1925V69.52H60.8245V62.688H65.1925V39.952H83.5605V36.424H61.9445V29.592H112.345V36.424H90.7285V39.952H109.097V62.688H113.465V69.52H109.097V79.6H94.3685ZM72.3605 62.688H83.5605V57.872H72.3605V62.688ZM72.3605 51.488H83.5605V46.784H72.3605V51.488ZM90.7285 62.688H101.929V57.872H90.7285V62.688ZM90.7285 51.488H101.929V46.784H90.7285V51.488ZM168.345 79.768C157.537 76.016 151.489 68.736 149.865 57.648H146.785V79.6H139.561V57.648H136.425C134.801 68.736 128.753 76.016 117.945 79.768V72.208C124.217 69.632 128.137 64.928 129.537 57.648H117.497V50.648H139.505V38.44H119.513V31.44H139.505V28.08H146.785V31.44H166.777V38.44H146.785V50.648H168.793V57.648H156.753C158.153 64.928 162.073 69.632 168.345 72.208V79.768ZM122.593 40.008H129.761L132.729 48.912H125.561L122.593 40.008ZM153.561 48.912L156.529 40.008H163.697L160.729 48.912H153.561ZM173.945 56.36V48.632H224.345V56.36H173.945ZM258.953 43.032H266.905V52.328C266.905 53.84 266.849 55.296 266.737 56.752C274.297 62.632 280.009 70.304 280.905 79.432H273.065C272.393 73.832 269.089 69.184 264.721 65.32C261.921 71.76 256.041 76.576 244.561 80.16V71.144C256.489 67.504 258.953 62.296 258.953 52.328V43.032ZM229.609 76.688H237.897C240.473 69.464 243.161 61.96 245.401 50.424H237.785C235.657 60.616 232.745 68.12 229.609 76.688ZM230.001 31.608L237.337 46.896H245.233L237.897 31.608H230.001ZM246.577 48.856C249.321 42.584 251.057 35.976 251.561 29.088H259.625C259.569 30.208 259.457 31.384 259.289 32.504H280.737C280.177 38.216 279.001 43.592 277.265 48.856H269.089C270.153 46.112 271.049 43.256 271.721 40.4H257.889C257.217 43.256 256.265 46.112 255.089 48.856H246.577Z" fill="white"/>
<path d="M92.3685 76.6V69.6H99.9285V66.52H70.3605V76.6H63.1925V66.52H58.8245V59.688H63.1925V36.952H81.5605V33.424H59.9445V26.592H110.345V33.424H88.7285V36.952H107.097V59.688H111.465V66.52H107.097V76.6H92.3685ZM70.3605 59.688H81.5605V54.872H70.3605V59.688ZM70.3605 48.488H81.5605V43.784H70.3605V48.488ZM88.7285 59.688H99.9285V54.872H88.7285V59.688ZM88.7285 48.488H99.9285V43.784H88.7285V48.488ZM166.345 76.768C155.537 73.016 149.489 65.736 147.865 54.648H144.785V76.6H137.561V54.648H134.425C132.801 65.736 126.753 73.016 115.945 76.768V69.208C122.217 66.632 126.137 61.928 127.537 54.648H115.497V47.648H137.505V35.44H117.513V28.44H137.505V25.08H144.785V28.44H164.777V35.44H144.785V47.648H166.793V54.648H154.753C156.153 61.928 160.073 66.632 166.345 69.208V76.768ZM120.593 37.008H127.761L130.729 45.912H123.561L120.593 37.008ZM151.561 45.912L154.529 37.008H161.697L158.729 45.912H151.561ZM171.945 53.36V45.632H222.345V53.36H171.945ZM256.953 40.032H264.905V49.328C264.905 50.84 264.849 52.296 264.737 53.752C272.297 59.632 278.009 67.304 278.905 76.432H271.065C270.393 70.832 267.089 66.184 262.721 62.32C259.921 68.76 254.041 73.576 242.561 77.16V68.144C254.489 64.504 256.953 59.296 256.953 49.328V40.032ZM227.609 73.688H235.897C238.473 66.464 241.161 58.96 243.401 47.424H235.785C233.657 57.616 230.745 65.12 227.609 73.688ZM228.001 28.608L235.337 43.896H243.233L235.897 28.608H228.001ZM244.577 45.856C247.321 39.584 249.057 32.976 249.561 26.088H257.625C257.569 27.208 257.457 28.384 257.289 29.504H278.737C278.177 35.216 277.001 40.592 275.265 45.856H267.089C268.153 43.112 269.049 40.256 269.721 37.4H255.889C255.217 40.256 254.265 43.112 253.089 45.856H244.577Z" fill="#A00000"/>
</svg>
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from 'vue'
import { cssAssetUrl } from '@/commons/assets.ts'
import MobileStage from '@/components/MobileStage.vue'
import { useGameSocket, joinSharedRoom, sendGameMessage, userJoinStatus } from '@/composables/useGameSocket'
import LoadingView from './views/LoadingView.vue'
import PlayingView from './views/PlayingView.vue'
import ScoreView from './views/ScoreView.vue'
import { $getWechat } from '@/commons/utils.ts'
type GameView = 'loading' | 'playing' | 'score'
const imageUrls = {
bg: cssAssetUrl('game4/h5-game4-bg.svg'),
bg2: cssAssetUrl('game1/bg2.png'),
playBg: cssAssetUrl('game4/h5-game4-bg2.svg'),
logo: cssAssetUrl('game1/logo.png'),
time: cssAssetUrl('game4/h5-game4-clock.svg'),
title: cssAssetUrl('game4/h5-game4-title1.svg'),
title2: cssAssetUrl('game4/h5-game4-title2.svg'),
avatarBg: cssAssetUrl('game4/h5-game4-join.svg'),
gift: cssAssetUrl('game4/h5-game4-gift.svg'),
avatar: cssAssetUrl('game1/avatar.png'),
gu: cssAssetUrl('game4/h5-game4-horse.svg'),
clock: cssAssetUrl('game1/clock.png'),
horse: cssAssetUrl('game4/h5-game4-horse.svg'),
yaoyiyao: cssAssetUrl('game4/h5-game4-yaoyiyao.svg'),
scoreBg: cssAssetUrl('game1/score-bg.png'),
back: cssAssetUrl('game4/back.svg'),
replay: cssAssetUrl('game4/replay.svg'),
close: cssAssetUrl('game4/h5-game4-close.svg'),
}
const wechat = $getWechat()
const currentView = ref<GameView>('loading')
const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const tick = ref(0) // 点击次数/摇动次数
const rank = ref(0) // 排名
const level = ref(5)
const currentHorse = ref(imageUrls.horse)
const currentYaoyiyao = ref(imageUrls.yaoyiyao)
const token = ref(wechat?.token ?? '')
const nickname = ref(wechat?.nickname ?? '')
const avatar = ref(wechat?.avatar ?? '')
const showGameRule = ref(false)
const mobileStageRef = ref<InstanceType<typeof MobileStage> | null>(null)
const _offSocketMessage = ref<(() => void) | undefined>()
let gameCountdownTimer: ReturnType<typeof window.setTimeout> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
event.preventDefault()
event.returnValue = ''
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
function stopGameCountdown() {
if (gameCountdownTimer) {
window.clearTimeout(gameCountdownTimer)
gameCountdownTimer = undefined
}
}
function submitScore(save: boolean = false) {
// 最后提交保存在数据库中
if (save) {
// item_num 游戏项目(1到6)
// wechat 原始token
sendGameMessage('submit_score_save', {
score: tick.value,
wechat: wechat.token_origin,
item_num: 4,
nickname: wechat.nickname,
avatar: wechat.avatar,
rank: rank.value,
})
} else {
sendGameMessage('submit_score', { score: tick.value })
}
}
function startGameCountdown(seconds = 60) {
stopGameCountdown()
countdownInterval.value = seconds
setDivDescVisible(true)
const tickCountdown = () => {
countdownInterval.value -= 1
if (countdownInterval.value <= 0) {
countdownInterval.value = 0
gameCountdownTimer = undefined
setDivDescVisible(false)
submitScore(true)
showScoreView()
return
}
gameCountdownTimer = window.setTimeout(tickCountdown, 1000)
}
gameCountdownTimer = window.setTimeout(tickCountdown, 1000)
}
function showLoadingView() {
stopGameCountdown()
currentView.value = 'loading'
setDivDescVisible(false)
}
function resetToLoadingView() {
stopGameCountdown()
tick.value = 0
rank.value = 0
countdownInterval.value = 60
currentHorse.value = imageUrls.horse
currentYaoyiyao.value = imageUrls.yaoyiyao
userJoinStatus.value = false
currentView.value = 'loading'
setDivDescVisible(false)
}
function startGameView() {
stopGameCountdown()
tick.value = 0
currentHorse.value = imageUrls.horse
currentView.value = 'playing'
startGameCountdown(60)
}
function showScoreView() {
stopGameCountdown()
setDivDescVisible(false)
level.value = rank.value
currentView.value = 'score'
}
function replayGame() {
startGameView()
}
function backToWaiting() {
resetToLoadingView()
joinSharedRoom(nickname.value, token.value, avatar.value)
}
function handleRoomBack() {
resetToLoadingView()
window.setTimeout(() => {
joinSharedRoom(nickname.value, token.value, avatar.value)
}, 200)
}
const touchHandler = () => {
tick.value += 1
submitScore()
}
const showGameRuleHandler = () => {
showGameRule.value = !showGameRule.value
}
if (wechat) {
const { offSocketMessage } = useGameSocket({
gameId: 'game4',
auth: {
userid: wechat.token,
},
onConnect: () => {
window.setTimeout(() => {
joinSharedRoom(nickname.value, token.value, avatar.value)
}, 200)
},
onGameStart: async () => {
showGameRule.value = false
await mobileStageRef.value?.startCountdown()
startGameView()
},
onScoreSubmitted: (is_save, data) => {
if (!is_save) {
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
if (Number.isFinite(nextRank) && nextRank > 0) {
rank.value = nextRank
}
}
},
onRescoreSubmitted: (_recount) => {
submitScore(true)
},
onRoomClosed: showLoadingView,
onRoomBack: handleRoomBack,
})
_offSocketMessage.value = offSocketMessage
}
onMounted(() => {
document.title = '小游戏 - 策马奔腾'
if (token.value) {
window.addEventListener('beforeunload', confirmRefresh)
}
})
onBeforeUnmount(() => {
_offSocketMessage.value?.()
stopGameCountdown()
if (token.value) {
window.removeEventListener('beforeunload', confirmRefresh)
}
})
</script>
<template>
<MobileStage v-if="token" :showGameRule="showGameRule" ref="mobileStageRef"
:background="`${imageUrls.bg} center/cover`">
<template #gameRule>
<div class="rule-container">
<div class="rule-txt-container">
<div class="rule-title">游戏规则</div>
<div class="rule-txt">
<!-- 微信用户通过扫描大屏幕二维码参与,可容纳多人同时进行,摇晃手机控制赛马奔跑,摇晃越快赛马跑得越快,游戏结束按分值高低先后排名,有机会获得新疆福彩定制周边礼品一份。 -->
1. 主持人开赛即可摇手机,单次摇晃得 5 分;
2. 得分越高大屏幕上面的马跑得越快;
3. 限时比拼摇速,结束后按积分排名;
4. 高分用户有机会领取新疆福彩定制周边;
5. 禁止外挂作弊,违规账号取消成绩与领奖资格;
</div>
</div>
<div class="rule-close">
<div @click="showGameRuleHandler"></div>
</div>
</div>
</template>
<LoadingView v-if="currentView === 'loading'" :image-urls="imageUrls" :user-join-status="userJoinStatus"
@touchGameRule="showGameRuleHandler" />
<PlayingView v-else-if="currentView === 'playing'" :image-urls="imageUrls" :tick="tick" :rank="rank"
:currentHorse="currentHorse" :currentYaoyiyao="currentYaoyiyao" :countdown-interval="countdownInterval"
:is-div-desc-visible="isDivDescVisible" @touch="touchHandler" />
<ScoreView v-else :image-urls="imageUrls" :tick="tick" :level="level" @replay="replayGame"
@back="backToWaiting" />
</MobileStage>
<div v-else style="text-align: center; width: 100vw; height: 100vh; line-height: 30; font-size: 20px;">
请使用微信扫码进入游戏
</div>
</template>
<style scoped>
.rule-container {
.rule-txt-container {
background: #FFF9E4;
margin: 0 40px;
border-radius: 5px;
padding-top: 20px;
padding-bottom: 10px;
.rule-title {
text-align: center;
color: #850000;
font-weight: bold;
font-size: 15pt;
}
.rule-txt {
font-size: 12pt;
padding: 10px;
margin-top: 10px;
}
}
.rule-close {
width: 32px;
height: 130px;
margin: 0 auto;
background: v-bind('imageUrls.close') center/cover;
position: relative;
>div {
position: absolute;
bottom: 0;
width: 32px;
height: 32px;
border-radius: 16px;
}
}
}
</style>
<script setup lang="ts">
defineProps<{
imageUrls: Record<string, string>
userJoinStatus: boolean
}>()
defineEmits<{
touchGameRule: []
}>()
</script>
<template>
<div class="h5-page game-stage">
<div class="game-desc" @click="$emit('touchGameRule')">游戏规则</div>
<div class="img-logo"></div>
<div class="img-title"></div>
<div class="img-title2"></div>
<div class="img-gift"></div>
<div class="img-avatar-bg"></div>
<div class="img-avatar"></div>
</div>
</template>
<style scoped>
.game-stage {
position: absolute;
width: 750px;
height: 1624px;
padding: 0;
}
.game-desc {
position: absolute;
top: 40%;
left: var(--stage-viewport-left, 0);
display: flex;
width: 58px;
height: 158px;
padding-top: 10px;
align-items: center;
justify-content: center;
border-radius: 0 12px 12px 0;
background: rgba(40, 12, 8, 0.52);
color: rgba(255, 255, 255, 0.9);
font-size: 25px;
letter-spacing: 10px;
line-height: 1.25;
text-align: center;
text-orientation: upright;
transform: translateY(-50%);
writing-mode: vertical-rl;
}
.bg-bottom {
position: absolute;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
height: 479px;
background: v-bind('imageUrls.bg2') center/cover;
opacity: 0.6;
}
.img-logo {
position: absolute;
top: 66px;
left: 50%;
width: 428px;
height: 77px;
background: v-bind('imageUrls.logo') center/cover;
transform: translateX(-50%);
}
.img-title {
position: absolute;
top: 200px;
left: 50%;
width: 609px;
height: 87.04px;
background: v-bind('imageUrls.title') center/cover;
transform: translateX(-50%);
}
.img-title2 {
position: absolute;
top: 290px;
left: 50%;
width: 491.77px;
height: 200px;
background: v-bind('imageUrls.title2') center/cover;
transform: translateX(-50%);
}
.img-gift {
position: absolute;
top: 421px;
right: -10px;
right: -10%;
width: 234.1px;
height: 142.21px;
background: v-bind('imageUrls.gift') center/cover;
}
.img-avatar-bg {
position: absolute;
top: 568px;
left: calc(50% + 12px);
width: 500px;
height: 821px;
background: v-bind('imageUrls.avatarBg') center/cover;
transform: translateX(-50%);
}
.img-avatar {
position: absolute;
top: 564px;
left: 50%;
width: 156px;
height: 156px;
background: v-bind('imageUrls.avatar') center/cover;
transform: translateX(-50%);
}
.txt-nickname {
position: absolute;
top: 650px;
left: 50%;
color: #E00000;
font-size: 25pt;
font-weight: bold;
transform: translateX(-50%);
}
.txt-loading {
position: absolute;
top: 710px;
left: 50%;
width: 288px;
color: #E00000;
font-size: 25pt;
font-weight: bold;
text-align: center;
transform: translateX(-50%);
}
.txt-loading label {
display: inline-block;
transform-origin: center;
animation: loading-text-scale 1.2s ease-in-out infinite;
}
@keyframes loading-text-scale {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.08);
}
}
.loading-gu {
position: absolute;
bottom: 100px;
left: 50%;
width: 600px;
height: 534px;
background: v-bind('imageUrls.gu') center/cover;
transform: translateX(-50%);
}
</style>
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue'
import shakeSound from '@/assets/images/game4/yaoyiyao.mp3'
defineProps<{
imageUrls: Record<string, string>
countdownInterval: number
isDivDescVisible: boolean
tick: number
rank: number
currentHorse: string
currentYaoyiyao: string
}>()
const emit = defineEmits<{
touch: []
}>()
// ========== 摇一摇 ==========
const shakeCooldown = 300
let lastShakeTime = 0
let lastPosition = {
x: 0,
y: 0,
z: 0,
time: Date.now(),
}
// 是否正在摇晃(用于停止检测)
let wasShaking = false
let audioContext: AudioContext | null = null
let shakeAudioBuffer: AudioBuffer | null = null
const initAudio = async () => {
try {
audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()
const response = await fetch(shakeSound)
const arrayBuffer = await response.arrayBuffer()
shakeAudioBuffer = await audioContext.decodeAudioData(arrayBuffer)
} catch (error) {
console.error('[摇一摇] 音频初始化失败:', error)
}
}
const playShakeSound = () => {
if (!audioContext || !shakeAudioBuffer) return
if (audioContext.state === 'suspended') {
audioContext.resume()
}
const source = audioContext.createBufferSource()
source.buffer = shakeAudioBuffer
source.connect(audioContext.destination)
source.start(0)
}
const handleDeviceMotion = (ev: DeviceMotionEvent) => {
const nowTime = Date.now()
const motion = ev.accelerationIncludingGravity
if (!motion || motion.x === null) return
const range = 50
if (nowTime - lastPosition.time > 100) {
const nowPosition = {
x: motion.x!,
y: motion.y!,
z: motion.z!,
}
// 两次摇晃的差值(位移法)
const dis =
Math.abs(nowPosition.x - lastPosition.x) +
Math.abs(nowPosition.y - lastPosition.y) +
Math.abs(nowPosition.z - lastPosition.z)
if (dis > range) {
wasShaking = true
const now = Date.now()
if (now - lastShakeTime < shakeCooldown) return
lastShakeTime = now
playShakeSound()
emit('touch')
}
// 用户停止摇晃(停止事件不需要处理)
if (dis < range && wasShaking) {
wasShaking = false
}
lastPosition.x = nowPosition.x
lastPosition.y = nowPosition.y
lastPosition.z = nowPosition.z
lastPosition.time = nowTime
}
}
window.addEventListener('devicemotion', handleDeviceMotion)
// ========== 生命周期 ==========
onMounted(async () => {
try {
await initAudio()
} catch (error) {
console.error('初始化失败:', error)
}
})
onBeforeUnmount(() => {
window.removeEventListener('devicemotion', handleDeviceMotion)
if (audioContext) {
audioContext.close()
}
})
</script>
<template>
<div class="h5-page game-stage">
<Transition name="div-desc-slide" appear>
<div v-if="isDivDescVisible" class="div-desc">
<span class="desc-clock" aria-hidden="true"></span>
<span class="desc-time">{{ countdownInterval }}</span>
</div>
</Transition>
<div class="img-logo"></div>
<div class="img-gift"></div>
<!-- <div class="img-time"></div> -->
<div class="play-content">
<div>当前得分</div>
<div>{{ tick }}</div>
<div>当前排名: &nbsp;&nbsp;<label>{{ rank }}</label>&nbsp;</div>
</div>
<!-- <div class="bg-bottom"></div> -->
<div class="bg-yaoyiyao" :style="{ background: `${currentYaoyiyao} center/cover` }"></div>
<div class="play-horse" :style="{ background: `${currentHorse} center/cover` }" @click="$emit('touch')"></div>
</div>
</template>
<style scoped>
.game-stage {
position: absolute;
width: 750px;
height: 1624px;
padding: 0;
}
.div-desc {
position: absolute;
top: 150px;
left: var(--stage-viewport-left, 0);
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
width: 180px;
height: 80px;
border-radius: 0 56px 56px 0;
background: rgba(40, 12, 8, 0.48);
color: #fff;
.desc-clock {
position: absolute;
left: 20px;
width: 38px;
height: 44px;
border-radius: 50%;
background: v-bind('imageUrls.clock') center/cover;
}
.desc-time {
position: absolute;
right: 40px;
color: white;
font-size: 35pt;
line-height: 1;
}
}
.div-desc-slide-enter-active {
transition:
transform 360ms ease-out 220ms,
opacity 360ms ease-out 220ms;
}
.div-desc-slide-leave-active {
transition:
transform 260ms ease-in,
opacity 260ms ease-in;
}
.div-desc-slide-enter-from,
.div-desc-slide-leave-to {
opacity: 0;
transform: translateX(-110%);
}
.div-desc-slide-enter-to,
.div-desc-slide-leave-from {
opacity: 1;
transform: translateX(0);
}
.bg-bottom {
position: absolute;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
height: 479px;
background: v-bind('imageUrls.bg2') center/cover;
opacity: 0.6;
}
.img-logo {
position: absolute;
top: 66px;
left: 50%;
width: 428px;
height: 77px;
background: v-bind('imageUrls.logo') center/cover;
transform: translateX(-50%);
}
.img-gift {
position: absolute;
top: 421px;
right: -10px;
right: -10%;
width: 234.1px;
height: 142.21px;
background: v-bind('imageUrls.gift') center/cover;
/* transform: scale(0.8); */
}
/* .img-time {
position: absolute;
top: 66px;
left: 50%;
width: 428px;
height: 77px;
background: v-bind('imageUrls.time') center/cover;
transform: translateX(-50%);
} */
.play-content {
position: absolute;
top: 250px;
left:0;
right: 0;
margin: 0 auto;
width: 556px;
padding: 30px 0;
border-radius: 25px;
background: rgba(40, 12, 8, 0.52);
color: white;
font-size: 28pt;
text-align: center;
}
.play-content>div:nth-child(2) {
display: inline-block;
margin: 10px 0;
background: linear-gradient(180deg, #FFE68D 0%, #FAD500 100%);
background-clip: text;
color: transparent;
font-size: 48pt;
font-weight: bold;
transform: scaleY(1.2);
transform-origin: center;
-webkit-background-clip: text;
}
.play-content>div:nth-child(3) {
position: relative;
top: -10px;
}
.play-content>div:nth-child(3) label {
color: white;
font-size: 42pt;
}
.bg-yaoyiyao {
position: absolute;
top: 698px;
left: 50%;
width: 514.51px;
height: 229px;
transform: translateX(-50%);
}
.play-horse {
position: absolute;
bottom: 164px;
left: 50%;
width: 597px;
height: 679px;
/* transform: translateX(-50%) scale(2.4); */
transform: translateX(-50%);
}
</style>
<script setup lang="ts">
defineProps<{
imageUrls: Record<string, string>
tick: number
level: number
}>()
defineEmits<{
replay: []
back: []
}>()
</script>
<template>
<div class="h5-page game-stage">
<div class="img-logo"></div>
<!-- <div class="bg-bottom"></div> -->
<div class="img-score">
<div class="score-avatar"></div>
<div class="score-content">
<div>张小明</div>
<div>您的成绩</div>
<div>策马跑道</div>
<div>{{ tick }}</div>
<div>最终排名</div>
<div><label>{{ level }}</label></div>
</div>
</div>
<div class="btn-group">
<div class="btn-replay" @click="$emit('replay')"></div>
<div class="btn-back" @click="$emit('back')"></div>
</div>
</div>
</template>
<style scoped>
.game-stage {
position: absolute;
width: 750px;
height: 1624px;
padding: 0;
}
.bg-bottom {
position: absolute;
bottom: 0;
left: var(--stage-viewport-left, 0);
width: var(--stage-viewport-width, 750px);
height: 479px;
background: v-bind('imageUrls.bg') center/cover no-repeat;
opacity: 0.6;
}
.img-logo {
position: absolute;
top: 66px;
left: 50%;
width: 428px;
height: 77px;
background: v-bind('imageUrls.logo') center/cover;
transform: translateX(-50%);
}
.img-score {
position: absolute;
top: 500px;
left: 50%;
width: 465px;
height: 731px;
background: v-bind('imageUrls.scoreBg') center/cover;
transform: translateX(-50%) scale(1.2);
}
.score-avatar {
position: absolute;
top: -20px;
left: 50%;
width: 60px;
height: 60px;
background: v-bind('imageUrls.avatar') center/cover;
transform: translateX(-50%) scale(2);
}
.score-content {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: calc(100% - 100px);
font-size: 28pt;
text-align: center;
}
.score-content > div:nth-child(1) {
color: #E00000;
font-size: 32pt;
font-weight: bold;
}
.score-content > div:nth-child(2) {
margin-top: 20px;
font-size: 32pt;
}
.score-content > div:nth-child(3) {
margin-top: 40px;
}
.score-content > div:nth-child(4) {
margin-top: 30px;
color: #A00000;
font-size: 32pt;
font-weight: bold;
transform: scale(2.4);
}
.score-content > div:nth-child(5) {
margin-top: 40px;
}
.score-content > div:nth-child(6) {
margin-top: 20px;
color: #A00000;
font-size: 32pt;
font-weight: bold;
}
.score-content > div:nth-child(6) label {
color: #A00000;
font-size: 42pt;
margin: 0 10px;
}
.btn-group {
position: absolute;
top: 1354px;
left: 50%;
display: flex;
gap: 40px;
transform: translateX(-50%);
}
.btn-replay,
.btn-back {
width: 315px;
height: 100px;
}
.btn-replay {
background: v-bind('imageUrls.replay') center/cover;
}
.btn-back {
background: v-bind('imageUrls.back') center/cover;
}
</style>
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import { $read } from '@/commons/utils' import { $read } from '@/commons/utils'
import Game1 from '@/pages/game1/Game.vue' import Game1 from '@/pages/game1/Game.vue'
import Game4 from '@/pages/game4/Game4.vue'
import Game6 from '@/pages/game6/Game.vue' import Game6 from '@/pages/game6/Game.vue'
const getWechatId = () => { const getWechatId = () => {
...@@ -23,6 +24,11 @@ const router = createRouter({ ...@@ -23,6 +24,11 @@ const router = createRouter({
// }, // },
}, },
{ {
path: '/game4',
name: 'Game4',
component: Game4,
},
{
path: '/game6', path: '/game6',
name: 'Game6', name: 'Game6',
component: Game6, component: Game6,
......
...@@ -36,6 +36,7 @@ export default defineConfig(({ command }) => ({ ...@@ -36,6 +36,7 @@ export default defineConfig(({ command }) => ({
copyImageAssets(), copyImageAssets(),
], ],
server: { server: {
host: true,
proxy: { proxy: {
'/socket.io': { '/socket.io': {
target: 'http://localhost:8082', target: 'http://localhost:8082',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment