浏览器原理 · 41/51
1. addEventListener 第三个参数 2. addEventListener 与 attachEvent 区别 3. 浏览器兼容性测试与内核 4. 浏览器兼容性问题 5. 浏览器内核与引擎 6. 浏览器图层创建条件 7. 浏览器多进程架构 8. 浏览器渲染机制 9. 浏览器存储方案 10. 浏览器版本检测方法 11. children 与 childNodes 区别 12. 常见浏览器兼容性问题 13. Chrome 页面进程数量 14. 坐标系统对比 15. 多标签页通讯方案 16. 删除 Cookie 17. 自定义事件 18. DOM 事件处理方式演进 19. 元素尺寸属性对比 20. DOM 节点操作 21. DOM 事件机制 22. addEventListener 与 attachEvent 的区别 23. 获取页面所有复选框 24. HTMLCollection 与 NodeList 区别 25. Hybrid 应用开发 26. 强缓存命中机制 27. 浏览器缓存机制 28. 页面编码与资源编码不一致处理 29. jQuery 事件绑定方法对比 30. Input 点击触发的事件顺序 31. JavaScript 浏览器兼容性问题 32. jQuery 多事件绑定实现 33. JSBridge 原理 34. 链接点击后 Hover 失效解决方案 35. 减少重绘和回流的性能优化 36. 移动端 300ms 点击延迟问题 37. 移动端视口配置 38. 移动端点击穿透问题解决 39. 移动端兼容性问题 40. JSBridge 原理与实现 41. 移动端 1px 像素问题解决方案 42. 浏览器渲染流程 43. 页面加载完成事件对比 44. Offset、Scroll、Client 属性对比 45. 同源策略与跨域解决方案 46. Script 标签位置对页面加载的影响 47. Service Worker 与 PWA 48. 存储方案对比:Cookie、Storage、IndexedDB 49. 强缓存默认时间 50. URL 到页面显示的完整过程 51. V8 引擎 JavaScript 执行过程

移动端 1px 像素问题解决方案

解决 Retina 屏幕下 CSS 1px 边框过粗的问题

问题

在 Retina 屏幕(DPR >= 2)上,CSS 的 1px 会被渲染成 2 个或更多物理像素,导致边框看起来比设计稿粗。如何让 1px 在高清屏上显示为真正的 1 物理像素?

解答

方案一:伪元素 + transform(推荐)

最常用的方案,兼容性好:

/* 单边框 */
.border-bottom {
  position: relative;
}

.border-bottom::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: #000;
  transform: scaleY(0.5);
  transform-origin: 0 0;
}

/* 四边框 */
.border-all {
  position: relative;
}

.border-all::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 200%;
  border: 1px solid #000;
  transform: scale(0.5);
  transform-origin: 0 0;
  box-sizing: border-box;
  pointer-events: none; /* 避免遮挡点击 */
  border-radius: 8px; /* 如需圆角,设为实际值的 2 倍 */
}

方案二:根据 DPR 动态设置

使用 JavaScript 检测 DPR,动态调整缩放比例:

// 获取设备像素比
const dpr = window.devicePixelRatio || 1;

// 设置 CSS 变量
document.documentElement.style.setProperty('--dpr', dpr);
document.documentElement.style.setProperty('--border-scale', 1 / dpr);
.border-bottom::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: #000;
  transform: scaleY(var(--border-scale, 0.5));
}

方案三:使用 box-shadow

简单但颜色会变浅:

.border-bottom {
  box-shadow: 0 1px 0 0 #000;
}

/* 0.5px 的阴影在 DPR=2 的屏幕上接近 1 物理像素 */
.border-bottom-thin {
  box-shadow: 0 0.5px 0 0 #000;
}

方案四:使用 SVG border-image

精确控制,但写法复杂:

.border-bottom {
  border-bottom: 1px solid transparent;
  border-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1' height='1'%3E%3Crect width='1' height='0.5' fill='%23000'/%3E%3C/svg%3E") 0 0 1 0 stretch;
}

方案五:viewport 缩放

整体缩放页面,需要配合 rem 使用:

<script>
  const dpr = window.devicePixelRatio || 1;
  const scale = 1 / dpr;
  
  // 设置 viewport
  document.querySelector('meta[name="viewport"]').setAttribute(
    'content',
    `width=device-width,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale},user-scalable=no`
  );
  
  // 设置根字体大小,配合 rem 使用
  document.documentElement.style.fontSize = dpr * 100 + 'px';
</script>
/* 使用 rem,1rem = 100px * dpr */
.box {
  width: 1rem; /* 实际 100px */
  border: 1px solid #000; /* 真正的 1 物理像素 */
}

封装通用 Sass Mixin

// 1px 边框 mixin
@mixin border-1px($color: #000, $direction: bottom, $radius: 0) {
  position: relative;
  
  &::after {
    content: '';
    position: absolute;
    pointer-events: none;
    
    @if $direction == bottom {
      left: 0;
      bottom: 0;
      width: 100%;
      height: 1px;
      background: $color;
      transform: scaleY(0.5);
    } @else if $direction == top {
      left: 0;
      top: 0;
      width: 100%;
      height: 1px;
      background: $color;
      transform: scaleY(0.5);
    } @else if $direction == all {
      top: 0;
      left: 0;
      width: 200%;
      height: 200%;
      border: 1px solid $color;
      border-radius: $radius * 2;
      transform: scale(0.5);
      transform-origin: 0 0;
      box-sizing: border-box;
    }
  }
}

// 使用
.list-item {
  @include border-1px(#e5e5e5, bottom);
}

.card {
  @include border-1px(#ddd, all, 8px);
}

关键点

  • 问题根源:DPR > 1 时,1 个 CSS 像素对应多个物理像素,导致边框变粗
  • 主流方案:伪元素 + hd18w scale 缩放,兼容性好,使用灵活
  • 缩放比例:DPR=2 时缩放 0.5,DPR=3 时缩放 0.33
  • 圆角处理:四边框方案中,border-radius 需设为实际值的 2 倍
  • pointer-events:伪元素要设置 pointer-events: none 避免遮挡交互