vue3个人网站电子宠物

预览

具体代码

Attack.gif

Attacked.gif

Static.gif

Walk.gif

javascript 复制代码
<template>
  <div class="pet-container" ref="petContainer">
    <p class="pet-msg">{{ pet.msg }}</p>
    <img ref="petRef" @click="debounce(attckPet, 150)()" class="pet" :src="pet.src" alt="" srcset="">
  </div>
</template>

<script setup lang="ts">
import { reactive, onMounted, ref } from 'vue'
import { debounce } from 'lodash'
const imgSrc = {
  Static: import('../../../assets/images/pet/Static.gif'),
  Attack: import('../../../assets/images/pet/Attack.gif'),
  Attacked: import('../../../assets/images/pet/Attacked.gif'),
  Walk: import('../../../assets/images/pet/Walk.gif'),
}
const pet = reactive({
  msg: '打我啊',
  src: imgSrc.Static,
})
const petContainer = ref<HTMLElement | null>()
const petRef = ref<HTMLElement | null>()
const mousePosition = reactive({
  x: 0,
  y: 0,
})
const petPosition = reactive({
  x: 0,
  y: 0,
})
const deg = ref<number>(0)
const deg_y = ref<number>(0)
const count = ref<number>(0)
const speed = 50
let timer = null
let isListenMouseMove = false
onMounted(async () => {
  changeSrc('Static', '打我啊')
})

const changeSrc = async (key, msg) => {
  let res = await imgSrc[key];
  pet.src = res.default
  pet.msg = msg
}

const attckPet = () => {
  if (isListenMouseMove) return
  changeSrc('Attacked', '啊!!!')
  window.addEventListener('mousemove', listenMouseMove)
  isListenMouseMove = true
  petPosition.x = petContainer.value?.offsetLeft
  petPosition.y = petContainer.value?.offsetTop
  setTimeout(() => {
    changeSrc('Walk', '我和你拼了')
    timer = setInterval(() => {
      move()
    }, 10);
  }, 300);
}

const listenMouseMove = (e: MouseEvent) => {
  // 需要移动的x轴距离 = 当前鼠标位置-距离浏览器左边的距离-宠物相对于浏览器页面宽度/2(宽的中心位置)
  mousePosition.x = e.x - petContainer.value.offsetLeft - petContainer.value.clientWidth / 2;
  mousePosition.y = e.y - petContainer.value.offsetTop - petContainer.value.clientHeight / 2;
  let speed = Math.ceil((Math.pow(mousePosition.x, 2) + Math.pow(mousePosition.y, 2)) / 1000);
  // 需要的旋转角度计算
  deg.value = 360 * Math.atan(mousePosition.y / mousePosition.x) / (2 * Math.PI);
  // 这里的event.clientX 返回当事件被触发时鼠标指针相对于浏览器页面(或客户区)的水平坐标。
  // 这里有关于图片位置的设置,注意你的gif图的方向,原图方向向左,那么这里就是小于,原图方向向右,这里就是大于。
  // 翻转图片
  if (petContainer.value.offsetLeft > e.clientX) {
    deg_y.value = - 180;
  } else {
    deg_y.value = 0;
  }
  //这里每一次移动鼠标都要重新计算距离,所以这里的count需要清零
  count.value = 0;
}

const move = () => {
  if (count.value < speed) {
    petPosition.x += mousePosition.x / speed
    petPosition.y += mousePosition.y / speed
    petRef.value.style.transform = "rotateZ(" + deg.value + "deg) rotateY(" + deg_y.value + "deg)"
    petContainer.value.style.left = petPosition.x + "px"
    petContainer.value.style.top = petPosition.y + "px"
    count.value++
  } else {
    window.removeEventListener('mousemove', listenMouseMove)
    changeSrc('Attack', '打死你')
    setTimeout(() => {
      changeSrc('Static', '打我啊')
      timer = clearInterval(timer);
      count.value = 0;
      isListenMouseMove = false;
    }, 1000);
  }
}
</script>

<style scoped lang="scss">
.pet-container {
  position: fixed;
  top: calc(100% - 100px);
  left: 0;

  .pet {
    width: 50px;
    height: 65px;
    cursor: pointer;
  }

  .pet-msg {
    padding: 5px;
    background: #8f8888;
    color: #fff;
  }
}
</style>

参考博客

参考博客

相关推荐
lovingsoft4 分钟前
Chrome Lighthouse 详解
前端·chrome
Qlly14 分钟前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝18 分钟前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦18 分钟前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡39 分钟前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI1 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战
汉堡大王95271 小时前
单次输出拉到100万Token:拆完 Gemini 4 Argon 的续写协议,才看懂谷歌这7个月在憋什么
前端·人工智能·后端
看浪的路人1 小时前
第9讲:大模型应用监控与可观测性——OpenTelemetry + Prometheus + Grafana 全链路追踪
java·前端·spring
内存过载1 小时前
用表格模拟甘特图:项目排期的土办法
前端·低代码
葡萄城技术团队1 小时前
用 SpreadJS 数据验证实现省市区三级联动
前端