移动端开发:h5应用开发

1. 修改index.html文件

html 复制代码
<meta name="viewport"
    content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />

2. 修改app.vue文件

设置html根元素字体大小:当屏幕宽度<=576px时,1rem = 1px;

html 复制代码
<script setup>
import { RouterView } from 'vue-router'
import { ref, onMounted, onBeforeUnmount } from 'vue'

// 判断是否横屏
const isLandscape = ref(false)

// 判断是否横屏
const checkLandscape = () => {
  isLandscape.value = (Math.abs(window.orientation) === 90)
}

// 设置html根元素字体大小:当屏幕宽度<=768px时,1rem = 1px;
// 手机<=480折叠屏手机768<=平板
const resize = () => {
  const deviceWidth = window.innerWidth
  // 375宽为视觉稿宽度
  document.documentElement.style.fontSize = window.innerWidth >= 768 ? `${768 / 375}px` : `${deviceWidth / 375}px`
}

onMounted(() => {
  resize()
  window.addEventListener('resize', resize)

  window.addEventListener('orientationchange', checkLandscape)
})

onBeforeUnmount(() => {
  window.removeEventListener('resize', resize)
  window.removeEventListener('orientationchange', checkLandscape)
})
</script>

<template>
  <RouterView v-show="!isLandscape" />
  <div v-show="isLandscape" class="tip">请切换至竖屏!</div>
</template>

<style scoped>
.tip {
  color: #1989fa;
  font-size: 40rem;
  font-weight: bold;
}
</style>

3. 修改main.css文件

css 复制代码
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-size: 14px;
  line-height: 20px;
}

html,
body,
#app {
  width: 100%;
  height: 100%;
}

#app {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #f5f5f5;
}

4. demo

html 复制代码
<template>
  <div class="page">
    <!-- 游戏画面 -->
    <div class="game-container">
      <canvas id="gameCanvas"></canvas>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import iceIcon from '@/assets/images/icons/ice.png'

let canvas  // Canvas 对象
let ctx // 渲染上下文
let offScreenCanvas // 离屏canvas
let offScreenCtx  // 离屏渲染上下文

const dpr = window.devicePixelRatio || 1 // 设备像素比
// 画布大小
let canvasWidth = 320 // px
let canvasHeight = 320  // px

// 已绘制的格子数,共 8 * 8 =  64
let gridFinishedNumber = 0

// 初始化canvas
function initCanvas() {
  // 获取canvas元素
  canvas = document.getElementById('gameCanvas')
  // 获取渲染上下文
  ctx = canvas.getContext('2d')

  // * 设备像素比,绘制出来的图片更清晰
  canvas.width = canvasWidth * dpr
  canvas.height = canvasHeight * dpr
  ctx.scale(dpr, dpr)

  // 离屏canvas
  offScreenCanvas = new OffscreenCanvas(canvasWidth * dpr, canvasHeight * dpr)
  // 获取离屏渲染上下文
  offScreenCtx = offScreenCanvas.getContext('2d')
  offScreenCtx.scale(dpr, dpr)

  // 绘制canvas
  drawCanvas()
}

// 绘制canvas
function drawCanvas() {
  // 清空画布
  clearCanvas()
  for (let i = 0; i < 8; i++) {
    for (let j = 0; j < 8; j++) {
      drawImage(iceIcon, j * 40, i * 40, 40, 40)
    }
  }
}

// 绘制图片
const drawImage = (image, x, y, width, height) => {
  const img = new Image()
  img.onload = () => {
    offScreenCtx.drawImage(img, x, y, width, height)
    gridFinishedNumber++
    if (gridFinishedNumber === 64) {
      console.log('绘制完成')
      ctx.clearRect(0, 0, canvasWidth, canvasHeight)
      // 绘制离屏canvas到主canvas
      ctx.drawImage(offScreenCanvas, 0, 0, canvasWidth, canvasHeight)
    }
  }
  img.src = image
}

// 异步绘制图片
const drawImageAsync = (image, x, y, width, height) => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = () => {
      offScreenCtx.drawImage(img, x, y, width, height)
      resolve()
    }
    img.src = image
  })
}

// 清空画布
function clearCanvas() {
  // 清空离屏canvas
  offScreenCtx.clearRect(0, 0, canvasWidth, canvasHeight)

  // 已绘制的格子数,共 8 * 8 =  64
  gridFinishedNumber = 0
}

onMounted(() => {
  // 初始化canvas
  initCanvas()
})
</script>

<style lang="scss" scoped>
.page {
  width: 375rem;
  height: 100%;
  padding: 10rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  background: white;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  overflow-x: hidden;
  overflow-y: auto;

  .game-container {
    position: relative;
    width: 320rem;
    height: 320rem;

    #gameCanvas {
      width: 320rem;
      height: 320rem;
      will-change: transform;
      transform: translate3d(0, 0, 0);
      /* 防止背面抖动 */
      backface-visibility: hidden;
      animation: slideInCard 1s ease-out forwards;
      z-index: 2;
    }
  }
}

@keyframes slideInCard {
  from {
    opacity: 0;
    transform: translateX(100%);
  }

  to {
    opacity: 1;
    transform: translateX(0);
  }
}
</style>
相关推荐
jerrywus21 分钟前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude
孟陬2 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
Zhao-Jichao2 小时前
AI Prompting for Everyone 课程笔记 - P17 图像生成 Imaging Generation
前端
Highcharts.js2 小时前
智能数据分析组件Orbit 正式发布与快速上手体验教程
前端·人工智能·信息可视化·数据分析系统·highcharts·orbit·智能可视化
螺丝钉tina3 小时前
[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试
前端·javascript·typescript
小天源4 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
Highcharts.js5 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格
薛定谔的猫-菜鸟程序员6 小时前
Electron开发超详细教程(从入门到放弃)
前端·javascript·electron
最强小杰6 小时前
DeepSeek V4 Flash + Codex CLI 接入教程:原生 Responses API 适配,从配置到跑通收藏这篇就够了(2026)
java·前端·python·ai
静水leo7 小时前
使用 Whistle 拦截接口并模拟后端响应
前端