ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

vue3项目-小兔鲜儿笔记-商品详情页01

2022-09-04 23:32:54  阅读:148  来源: 互联网

标签:遮罩 01 const 鼠标 容器 鲜儿 value 详情页 changeResult


1. 基础布局

完成商品详情基础布局,路由配置,搭好页面架子

 

 

2. 渲染面包屑

编写一个钩子函数useGoods.js,将面包屑获取数据的逻辑抽取出来。

// 拿到商品信息
import { findGoods } from '@/api/product'
import { nextTick, ref, watch } from 'vue'
import { useRoute } from 'vue-router'

const useGoods = () => {
  const route = useRoute()

  const goods = ref(null)

  watch(
    () => route.params.id,
    (newVal) => {
      // 先判断路由是否为/product/id,是这个路由才发送查找商品详情的请求
      if (newVal && `/product/${newVal}` === route.path) {
        findGoods(newVal).then(({ result }) => {
          // 先置空,让依赖于goods的组件(v-if=goods)这些组件可以先销毁再创建
          goods.value = null
          nextTick(() => {
            goods.value = result
          })
        })
      }
    },
    { immediate: true }
  )

  return {
    goods
  }
}

export default useGoods

// 面包屑组件中使用:
// goods-bread:
const { goods } = useGoods()

 

3.图片放大镜

  • 首先准备大图容器和遮罩容器

  • 使用@vueuse/core的useMouseInElement方法获取基于容器的偏移量elementX和elementY,以及是否鼠标在容器范围外isOutside

  • 通过调整遮罩容器的left和top定位以及调整大图容器的背景图片的backgroundPositionX和backgroundPositionY定位,实现图片放大镜的功能。

// 大图容器  
.large {
    position: absolute;
    top: 0;
    left: 412px;
    width: 400px;
    height: 400px;
    box-shadow: 0 0 10px rgba(0, 8, 8, 0.1);
    background-repeat: no-repeat;
    background-size: 800px 800px;
    background-color: #f8f8f8;
  }

// 遮罩容器
 .layer {
      position: absolute;
      left: 0;
      top: 0;
      width: 200px;
      height: 200px;
      background: rgba(0, 0, 0, 0.2);
    }

 

抽取图片放大镜逻辑到usePreviewImage.js中,使得图片预览组件没有那么臃肿

初始数据:
  // 要观察的容器
  const target = ref(null)
  // 是否显示遮罩层和大图背景
  const show = ref(false)
  // 遮罩层坐标(左上角),因为遮罩层是绝对定位,设置成left、top
  const layerPosition = reactive({
    left: 0,
    top: 0
  })
  // 大图背景坐标(左上角),因为大图是背景定位,设置成backgroundPositionX
  const largeImagePosition = reactive({
    backgroundPositionX: 0,
    backgroundPositionY: 0
  })

大致步骤:

  • 明确数据的含义

    • elementX 鼠标基于容器X轴的偏移量

    • elementY 鼠标基于容器Y轴的偏移量

    • isOutside 鼠标是否在容器外部

    • 这三个数据都是响应式变量

  • 监听这三个数据,让遮罩容器的坐标和大图背景坐标随着偏移量的变化而变化

    • 如何让鼠标位于遮罩容器的中心?遮罩容器左移和上移自身的一半,这里是减去100

    • 因为遮罩容器不能超出图片容器的范围,所以遮罩容器的左上角X轴最多移动距离:0~200

    • 因为大图背景显示的就是鼠标指的位置,且受限遮罩容器X轴的范围:0~200,导致鼠标最多移动范围:100~300。而大图背景移动方向是与鼠标相反的,鼠标往右,背景往左移,所以大图背景坐标移动的范围:-300~-100

  // elementX:鼠标基于容器X轴的偏移量
  // elementY: 鼠标基于容器Y轴的偏移量
  // isOutside: 鼠标是否在容器外部,这三个数据都是响应式变量
  const { elementX, elementY, isOutside } = useMouseInElement(target)
  // 监听这三个数据,让遮罩层坐标和大图背景坐标随着偏移量的变化而变化
  watch([elementX, elementY, isOutside], () => {
    show.value = !isOutside.value
    // 让鼠标位于遮罩层中心,就是让遮罩层左移和上移
    // 遮罩层的坐标范围:0~200,大图背景坐标范围:-300~-100,因为大图背景随着鼠标移动而移动,鼠标往右,背景往左移
    layerPosition.left = elementX.value - 100 + 'px'
    layerPosition.top = elementY.value - 100 + 'px'
    largeImagePosition.backgroundPositionX = -elementX.value + 'px'
    largeImagePosition.backgroundPositionY = -elementY.value + 'px'
    if (elementX.value <= 100) {
      layerPosition.left = 0
      largeImagePosition.backgroundPositionX = '-100px'
    }
    if (elementY.value <= 100) {
      layerPosition.top = 0
      largeImagePosition.backgroundPositionY = '-100px'
    }
    if (elementX.value >= 300) {
      layerPosition.left = '200px'
      largeImagePosition.backgroundPositionX = '-300px'
    }
    if (elementY.value >= 300) {
      layerPosition.top = '200px'
      largeImagePosition.backgroundPositionY = '-300px'
    }
  })

 

4. 城市组件的封装

初始数据:
const target = ref(null)
const visible = ref(false) // 控制窗口是否显示
const loading = ref(false) // 数据加载中状态
const cityData = ref([]) // 地区数组
// 根据点击的地区列表的按钮动态更改选择的地区结果
const changeResult = reactive({
  provinceCode: '',
  provinceName: '',
  cityCode: '',
  cityName: '',
  countyCode: '',
  countyName: '',
  fullLocation: ''
})
// 获取地址数据
// 1.地址在窗口打开的时候才获取
// 2.获取了地址之后要做全局缓存,因为数据量太大
// 3.获取了缓存之后下次打开窗口就直接从缓存中取,因此数据有可能是异步也有可能是同步获取,需要使用promise判断
const getCityData = () => {
  return new Promise((resolve, reject) => {
    if (window.cityData) {
      resolve(window.cityData)
    } else {
      const url =
        'https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json'
      axios.get(url).then(({ data }) => {
        window.cityData = data
        resolve(data)
      })
    }
  })
}
开关窗口的逻辑
const open = () => {
  visible.value = true
  // 获取地址数据
  loading.value = true
  getCityData().then((res) => {
    cityData.value = res
    loading.value = false
  })
}
const close = () => {
  visible.value = false
}
// 控制窗口是否显示
const toggleDialog = () => {
  visible.value ? close() : open()
}
// 当鼠标点击外部,关闭窗口
onClickOutside(target, () => {
  close()
})
点击地区组件的逻辑
// 点击地区列表的按钮时,根据点的是省还是市还是区做判断
const changeItem = (item) => {
  if (item.level === 0) {
      // 如果点击的是省,就拿到这个省的代码
    changeResult.provinceCode = item.code
    changeResult.provinceName = item.name
  }
  if (item.level === 1) {
      // 如果是点击的是市,就拿到这个市的代码
    changeResult.cityCode = item.code
    changeResult.cityName = item.name
  }
  // 如果已经点到区了,就说明是地址已经全部点完,可生成完整地址,并通知父组件地址已改变
  if (item.level === 2) {
    changeResult.countyCode = item.code
    changeResult.countyName = item.name
    changeResult.fullLocation = `${changeResult.provinceName} ${changeResult.cityName} ${changeResult.countyName}`
    // 把点击完生成的地址结果传给父组件
    emit('change', changeResult)
    close()
  }
}
地区列表的显示:
// 地区列表中显示的不一定是省的数据,可能会是市和区
// 但是会依赖于cityData来动态显示,根据已动态更改的地区结果改变渲染的地区列表
const currList = computed(() => {
  let list = cityData.value // 一开始默认展示的省列表
  // 可能是市,如果点击了某个省,就通过省代码找到这个省,获得这个省下的市列表进行展示
  if (changeResult.provinceCode) {
    list = list.find(
      (province) => province.code === changeResult.provinceCode
    ).areaList
  }
  // 可能是区,如果点击了某个市,就通过市代码找到这个市,获得这个市下的区列表进行展示
  if (changeResult.cityCode) {
    list = list.find((city) => city.code === changeResult.cityCode).areaList
  }
  return list
})

 

标签:遮罩,01,const,鼠标,容器,鲜儿,value,详情页,changeResult
来源: https://www.cnblogs.com/jzhFlash/p/16656487.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有