如何用JS 判断 iPhone X Series 机型

本文介绍了一种判断当前设备是否为iPhone X及其后续机型的方法,通过检查设备像素比、屏幕宽度和高度来实现精确识别。同时,提供了简化版的判断逻辑,仅基于iOS系统和屏幕高度特征。此外,还探讨了媒体查询在适配过程中的局限性,并强调了结合JS判断的重要性。

对每个机型进行判断

const isIphonex = () => {
  // X XS, XS Max, XR
  const xSeriesConfig = [
    {
      devicePixelRatio: 3,
      width: 375,
      height: 812,
    },
    {
      devicePixelRatio: 3,
      width: 414,
      height: 896,
    },
    {
      devicePixelRatio: 2,
      width: 414,
      height: 896,
    },
  ];
  // h5
  if (typeof window !== 'undefined' && window) {
    const isIOS = /iphone/gi.test(window.navigator.userAgent);
    if (!isIOS) return false;
    const { devicePixelRatio, screen } = window;
    const { width, height } = screen;
    return xSeriesConfig.some(item => item.devicePixelRatio === devicePixelRatio && item.width === width && item.height === height);
  }
  return false;
}

统一处理方法
因为现在 iPhone 在 iPhone X 之后的机型都需要适配,所以可以对 X 以后的机型统一处理,我们可以认为这系列手机的特征是 ios + 长脸。

在 H5 上可以简单处理。

const isIphonex = () => {
  if (typeof window !== 'undefined' && window) {
    return /iphone/gi.test(window.navigator.userAgent) && window.screen.height >= 812;
  }
  return false;
};
媒体查询
@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
}
@media only screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) {
}
@media only screen and (device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) {
}

媒体查询无法识别是不是 iOS,还得加一层 JS 判断,否则可能会误判一些安卓机。
原文作者:axuebin

<template> <!--首次加载数据效果--> <up-loading-page loadingText="" :loading="onloading" bgColor="#fff" iconSize="80px" zIndex="999" image="https://m.nocexpo.com/statics/js/modules/h5/jinn/loading.gif"> </up-loading-page> <!-- <root> --> <root v-if="!onloading"> <gb :message="gbConfig.message"></gb> <view class="container" :style="`height: ${screenArInfo.middleHeight}px;overflow:hidden;`"> <view scroll-y class="scroll-view"> <view class="head" :style="`width:${headSize.right}px`"> <view class="first"> <view class="lan"></view> <view class="first-c"> <view>仅看差异</view> <view class="switch-container"> <switch :checked="showOnlyDiff" @change="toggleDiffMode" color="rgba(16, 98, 202, 1)" style="transform:scale(0.7)"></switch> </view> </view> <view class="lan"></view> </view> <view class="data"> <view class="data-c"> <view class="item" v-for="(product, index) in products" :key="index" :class="dingFlag && index === 0?'item_first':''"> <!--固定右侧的小白条--> <view class="ding-right"></view> <view :class="`item-c item-${index}`"> <view class="icon"> <view class="din" @click.stop="toggleDing(index)"> <pic v-if="dingFlag && index === 0" src="icon_ding_active.png" prefix="image" cstyle="width:25.44rpx;height:25.42rpx" /> <pic v-else src="icon_ding.png" prefix="image" cstyle="width:25rpx;height:25rpx" /> {{ dingFlag && index === 0 ? '钉住' : '钉在左侧' }} </view> <!-- 使用view包装pic组件以确保事件能正确触发 --> <view @click.stop="removeProduct(index)" class="close"> <pic src="icon_close.png" prefix="image" cstyle="width:16.45rpx;height:16.01rpx" /> </view> </view> <view class="title"> {{ product.seriesName }} </view> <view class="grey"> <!-- {{ product.brand }} --> {{ product.name }} </view> </view> </view> <!-- <view class="last" @click="addProduct"> <view>添加机型</view> <view class="i-img"> <pic src="icon_add.png" prefix="image" cstyle="width:34rpx;height:34rpx" /> </view> </view>--> </view> </view> </view> <!-- <view class="hr"></view> --> <view class="data-list"> <view class="datas"> <view class="group-name" :style="`width:${headSize.right-5}px`"> <view>参数</view> </view> <view class="group-data" :style="`width:${headSize.right-5}px`"> <view class="data"> <view class="item"> <view class="d">品牌</view> <view class="d" v-for="(product, index) in products" :key="index" :class="dingFlag && index==0?'item_first':''"> {{ product.brand }} </view> </view> <view class="item"> <view class="d">价格</view> <view class="d" v-for="(product, index) in products" :class="dingFlag && index==0?'item_first':''"> {{ convertPrice(product) }} </view> </view> </view> </view> </view> <view class="datas" v-for="(group, groupIndex) in filteredAttributeGroups" :key="groupIndex"> <view class="group-name" :style="`width:${headSize.right-5}px`"> <view>{{ group.groupName }}</view> </view> <view class="group-data" :style="`width:${headSize.right-5}px`"> <view class="data"> <view class="item" v-for="(attr, attrIndex) in group.attributes" :key="attrIndex"> <view class="d">{{ attr.attrName }}</view> <view class="d" v-for="(product, productIndex) in products" :key="productIndex" :class="`${attr.isDiff?'diff':''} ${dingFlag && productIndex==0?'item_first':''}`"> {{ getProductAttributeValue(product, group.groupName, attr.attrName) }} </view> </view> </view> </view> </view> </view> <view v-if="!screenArInfo.boolStripe" style="height: 20rpx;"></view> </view> </view> </root> </template> <script setup> import { ref, reactive, computed, onMounted } from "vue" import { useStore } from 'vuex'; import infoApi from '@/api/product/equipment/info.js'; import utilApi from "@/utils/util.js"; import classifyApi from '@/api/productseries.js'; const gbConfig = ref({ psize: {}, message: { type: 1, TitleText: "设备参数", url: "pages/Choose/parameter/index" } }) const onloading = ref(true); const dingFlag = ref(false); const showOnlyDiff = ref(false); // 控制是否仅显示差异 const products = ref([]); const attributeGroups = ref([]); const screenArInfo = computed(() => { return useStore().getters.getScreenInfo }) const headSize = ref({}); const productIds = ref(''); // 计算属性,根据showOnlyDiff过滤属性组 const filteredAttributeGroups = computed(() => { if (!showOnlyDiff.value) { // 如果不显示差异,则返回所有属性组 return attributeGroups.value; } else { // 如果仅显示差异,则过滤掉isDiff为false的属性组和属性 return attributeGroups.value .filter(group => group.isDiff) // 过滤属性组 .map(group => { // 过滤属性 const filteredAttributes = group.attributes.filter(attr => attr.isDiff); return { ...group, attributes: filteredAttributes }; }) .filter(group => group.attributes.length > 0); // 过滤掉没有属性的组 } }); // 根据系列id获取所有型号id const dataAll = ref([]); const IDS = ref(''); const getSeriesId = async (id) => { const params = { seriesId: id, } try { const res = await classifyApi.getAllId(params); if (res.data.code === 0) { console.log(res.data.list, 'res---'); // 检查数据结构,确保products正确赋值 if (Array.isArray(res.data.list)) { // 假设res.data.list中的每个item需要映射到products的格式 // 创建一个临时数组存储转换后的数据 const formattedProducts = []; // 遍历响应数据 res.data.list.forEach(item => { // 检查item是否包含products数组 if (item.products && Array.isArray(item.products)) { // 将每个product添加到formattedProducts中,并确保包含模板中使用的属性 item.products.forEach(product => { // 确保产品对象包含必要的属性,如seriesName, name, brand等 formattedProducts.push({ id: product.id, seriesName: product.seriesName || item.seriesName || '未知系列', name: product.name || '未知型号', brand: product.brand || '未知品牌', quoted: product.quoted || 2, // 默认暂无报价 price: product.price || 0, attributes: product.attributes || {} }); }); } else { // 如果item本身就是产品对象,则直接添加 formattedProducts.push({ id: item.id, seriesName: item.seriesName || '未知系列', name: item.name || '未知型号', brand: item.brand || '未知品牌', quoted: item.quoted || 2, price: item.price || 0, attributes: item.attributes || {} }); } }); // 赋值给products products.value = formattedProducts; // 重新生成attributeGroups if (formattedProducts.length > 0) { regenerateAttributeGroups(); } console.log('处理后的产品数据:', products.value); } // 计算高度 setTimeout(() => { getHeight() }, 100); } onloading.value = false; // 加载完成后关闭加载状态 } catch (error) { console.error('获取系列型号ID失败:', error); onloading.value = false; // 出错时也关闭加载状态 } } /** * 切换差异显示模式 */ function toggleDiffMode(event) { showOnlyDiff.value = event.detail.value; } /** * 添加产品 */ function addProduct() { uni.navigateTo({ url: '/pages/product/equipment/list/list' }) } /** * 删除指定索引的产品 */ function removeProduct(index) { // 检查产品数量是否大于1,否则不允许删除 if (products.value.length <= 1) { console.log('至少保留一个设备'); // 这里可以添加用户提示,比如使用uni.showToast uni.showToast({ title: '至少保留一个设备', icon: 'none', duration: 2000 }); return; } // 检查索引是否有效 if (index < 0 || index >= products.value.length) { console.error('删除型号无效:', index); return; } // 从products数组中删除指定索引的产品 products.value.splice(index, 1); console.log('products after removal:', products.value); // 重新生成attributeGroups数据 regenerateAttributeGroups(); // 确保onloading状态正确(在某些情况下可能需要) onloading.value = false; } /** * 钉住/取消钉住功能 * @param {Number} index - 要钉住的产品索引 */ async function toggleDing(index) { // 如果点击的是第一个产品(索引为0) if (index === 0) { // 切换钉住状态 dingFlag.value = !dingFlag.value; // 如果钉住状态为true,确保该产品保持在第一位 if (dingFlag.value && products.value.length > 1) { // 第一个产品已经是索引0,无需移动 console.log('产品已钉住'); } } else { // 如果点击的不是第一个产品,则需要将该产品移动到第一位并钉住 if (index >= 0 && index < products.value.length) { // 获取要移动的产品 const productToMove = products.value[index]; // 从原位置删除并添加到第一位 products.value.splice(index, 1); products.value.unshift(productToMove); // 设置钉住状态 dingFlag.value = true; // 重新生成属性组 regenerateAttributeGroups(); console.log('产品已移动到第一位并钉住'); } } } /** * 根据当前products重新生成attributeGroups */ function regenerateAttributeGroups() { // 创建一个映射来存储所有属性组和属性 const attributeMap = new Map(); // 遍历所有产品,收集所有的属性组和属性 products.value.forEach(product => { if (product.attributes) { Object.keys(product.attributes).forEach(groupName => { // 如果属性组不存在,则创建它 if (!attributeMap.has(groupName)) { attributeMap.set(groupName, new Set()); } // 将该组下的所有属性添加到集合中 const groupAttrs = product.attributes[groupName]; Object.keys(groupAttrs).forEach(attrName => { attributeMap.get(groupName).add(attrName); }); }); } }); // 将Map转换为与原来格式相同的数组 const newAttributeGroups = []; attributeMap.forEach((attrsSet, groupName) => { const attributes = []; attrsSet.forEach(attrName => { // 检查该属性是否在所有产品中都有不同的值 let isDiff = checkAttributeIsDiff(groupName, attrName); attributes.push({ attrName: attrName, isDiff: isDiff }); }); // 检查该属性组是否在不同产品中有不同的属性 let groupIsDiff = attributes.some(attr => attr.isDiff); newAttributeGroups.push({ groupName: groupName, attributes: attributes, isDiff: groupIsDiff }); }); // 更新attributeGroups attributeGroups.value = newAttributeGroups; // 重新计算高度 // setTimeout(() => { // getHeight(); // }, 1000); } /** * 检查指定属性在所有产品中是否具有不同值 */ function checkAttributeIsDiff(groupName, attrName) { let values = []; // 收集所有产品的该属性值 products.value.forEach(product => { if (product.attributes && product.attributes[groupName] && product.attributes[groupName][attrName]) { values.push(product.attributes[groupName][attrName]); } else { values.push(undefined); } }); // 检查是否所有定义的值都相同 const firstValue = values[0]; return !values.every(value => value === firstValue); } /** * 获取对比数据 */ // async function fetchContrastData() { // if (!productIds.value) return; // try { // const response = await infoApi.getproductcontrast(productIds.value) // if (response.data.code === 0) { // products.value = response.data.list[0].products || [] // attributeGroups.value = response.data.list[0].attributeGroups || [] // onloading.value = false; // 关闭加载动画 // // 计算高度 // setTimeout(() => { // getHeight() // }, 100); // } // } catch (error) { // console.error('获取对比数据失败:', error) // onloading.value = false; // 出错时也关闭加载动画 // } // } function convertPrice(item) { if (item.quoted === 1) { return "¥" + utilApi.handlePrice(item.price) + "起" } else if (item.quoted === 0) { return "询价" } else { return "暂无报价" } } /** * 此方法数据加载后执行,获取元素成开的高度 */ function getHeight() { const query = uni.createSelectorQuery(); // query.selectAll('.head').boundingClientRect(data => { // headSize.value = data[0] // }).exec(); // console.log(headSize.value.right, 2222222222, ".item-" + (products.value.length - 1)); if (!headSize.value.right) { query.selectAll('.item-' + (products.value.length - 1)).boundingClientRect(data => { headSize.value = data[0]; }).exec(); } } function getProductAttributeValue(product, groupName, attrName) { if (product.attributes && product.attributes[groupName] && product.attributes[groupName][attrName] && product.attributes[groupName][attrName].length > 0) { return product.attributes[groupName][attrName]; } else { return '-'; } } // 存储页面参数 const pageOptions = ref({}); onMounted(() => { // 获取URL参数 const pages = getCurrentPages() const currentPage = pages[pages.length - 1] pageOptions.value = currentPage.options || {} productIds.value = pageOptions.value.productIds || '' // 确保有seriesId参数再调用 if (pageOptions.value.seriesId) { getSeriesId(pageOptions.value.seriesId); } else { console.warn('缺少seriesId参数'); onloading.value = false; } }); </script> <style lang="scss" scoped> .container { background-color: #fff; } .scroll-view { height: 100%; overflow-y: auto; .data-list { padding-top: 10rpx; padding-bottom: 2rpx; .datas { .group-name { font-size: 28rpx; line-height: 54rpx; font-weight: 600; display: flex; view:first-child { position: sticky; left: 26rpx; } } .group-data { display: flex; align-items: center; padding-right: 16rpx; position: relative; box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); .data { border-top: 2rpx solid rgba(209, 224, 255, 1); .item { display: flex; padding-left: 26rpx; .d { display: flex; justify-content: center; align-items: center; flex: 0 0 auto; text-align: center; width: 200rpx; flex-shrink: 0; padding: 10rpx 16rpx; font-size: 24rpx; min-height: 60rpx; color: rgba(56, 56, 56, 1); border-right: 2rpx solid rgba(209, 224, 255, 1); border-bottom: 2rpx solid rgba(209, 224, 255, 1); word-wrap: break-word; word-break: break-word; white-space: pre-wrap; .ding-right { display: none; } } .d:last-child { margin-right: 0px; } .d:first-child { position: sticky; left: 26rpx; flex-shrink: 0; width: 175rpx; flex: 0 0 auto; font-size: 28rpx; z-index: 4; color: rgba(0, 0, 0, 1); background: rgba(239, 244, 255, 1); border-left: 2rpx solid rgba(209, 224, 255, 1); &:before { position: absolute; content: ""; z-index: 3; top: -4%; left: -28rpx; height: 106%; width: 26rpx; background-color: #fff; } } .d.diff { background: rgba(255, 245, 242, 1); color: rgba(16, 98, 202, 1); } } .item_first { position: sticky; left: 233rpx; background-color: #fff; } } } } } } .hr { box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); position: fixed; left: 0px; top: 363rpx; width: 100%; z-index: 10; height: 2rpx; background: #fff; } .head { display: flex; padding: 0rpx 0px 18rpx 0rpx; position: sticky; top: 0px; z-index: 9; height: 160rpx; background-color: #fff; box-shadow: 0px 4rpx 6rpx rgba(0, 0, 0, 0.1); .first { position: sticky; left: 0rpx; display: flex; .lan { width: 36rpx; height: 165rpx; position: relative; z-index: 8; background-color: #fff; margin-right: -10rpx; } .lan:last-child { width: 24rpx; margin-left: -10rpx; margin-right: 0px; } .first-c { width: 200rpx; height: 160rpx; flex: 0 0 auto; z-index: 9; border-radius: 12rpx; background: rgba(255, 245, 242, 1); color: rgba(0, 0, 0, 1); font-size: 24rpx; text-align: center; border: 2rpx solid rgba(255, 230, 224, 1); view:first-child { font-size: 24rpx; padding: 36rpx 0px 0px; } view:last-child { display: flex; align-items: center; justify-content: center; height: 46rpx; margin-top: 16rpx; } } } .data { flex: 0 0 auto; .data-c { width: 100%; display: flex; align-items: center; .item { flex-shrink: 0; margin: 0px 7rpx; .ding-right { display: none; } .item-c { width: 208rpx; height: 160rpx; padding: 0px 0rpx 0rpx 10rpx; display: inline-block; border-radius: 12rpx; background: rgba(239, 244, 255, 1); border: 2rpx solid rgba(209, 224, 255, 1); .icon { display: flex; justify-content: space-between; .din { display: flex; align-items: center; font-size: 20rpx; gap: 10rpx; color: rgba(97, 97, 97, 1); } .close { padding: 15rpx; } } .title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: rgba(46, 45, 45, 1); font-size: 24rpx; font-weight: 600; padding-right: 10rpx; } .grey { word-wrap: break-word; height: 70rpx; word-break: normal; font-size: 24rpx; margin-top: 5rpx; line-height: 35rpx; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; /* 限制最多显示3行 */ overflow: hidden; text-overflow: ellipsis; /* 可选,部分浏览器不生效 */ word-break: break-word; color: rgba(56, 56, 56, 1); padding-right: 10rpx; } } } .item:first-child { margin-left: 0px; } .item_first { position: sticky; left: 242rpx; margin-left: 0px; .item-c { position: relative; z-index: 4; } .ding-right { position: absolute; width: 16rpx; height: 100%; right: -10rpx; top: 0px; z-index: 3; display: block; background: #fff; } } } } } .param-title { color: rgba(0, 0, 0, 1); font-size: 28rpx; font-weight: 555; padding: 20rpx 26rpx; } </style>在安卓正常显示 在ios会出现横向滚动时纵向也会滚动的情况 分析代码 找出什么原因
最新发布
10-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值