移动端1px细线和图片高清显示方案

438 阅读3分钟

1. 移动端1px细线高清显示。

问题分析: 对于CSS而言,多倍屏下能显示的最小单位可以认为是border: 0.5px。然而,并不是所有手机浏览器都能识别border: 0.5px,在有的系统里,0.5px会被当成为0px处理,那么我们需要做的就是实现这0.5px。

可行性分析: 方案1: 构建1个伪元素, border为1px, 再以transform缩放到50%。

/**
 * 移动端1px细线
 */

@border-color: rgba(0, 0, 0, 0.1);
.border-1px,
.border-top-1px,
.border-bottom-1px,
.border-top-bottom-1px,
.border-left-1px,
.border-right-1px {
  position: relative;
}

// 全边框
.border-1px {
  &:before {
    .allBorder();
  }
}

// 上边框
.border-top-1px {
  &:before {
    .topBorder();
  }
}

// 下边框
.border-bottom-1px {
  &:after {
    .bottomBorder();
  }
}

// 上下边框
.border-top-bottom-1px {
  &:before {
    .topBorder();
  }
  &:after {
    .bottomBorder();
  }
}

// 左边框
.border-left-1px {
  &:before {
    .leftBorder();
  }
}

// 右边框
.border-right-1px {
  &:after {
    .rightBorder();
  }
}

.allBorder(@color: @border-color) {
  content: " ";
  position: absolute;
  left: 0;
  top: 0;
  width: 200%;
  border: 1px solid @color;
  color: @color;
  height: 200%;
  transform-origin: left top;
  transform: scale(0.5);
}

.topBorder(@color: @border-color) {
  content: " ";
  position: absolute;
  left: 0;
  top: 0;
  right: 0;
  height: 1px;
  border-top: 1px solid @color;
  color: @color;
  transform-origin: 0 0;
  transform: scaleY(0.5);
}

.bottomBorder(@color: @border-color) {
  content: " ";
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  height: 1px;
  border-bottom: 1px solid @color;
  color: @color;
  transform-origin: 0 100%;
  transform: scaleY(0.5);
}

.leftBorder(@color: @border-color) {
  content: " ";
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  bottom: 0;
  border-left: 1px solid @color;
  color: @color;
  transform-origin: 0 0;
  transform: scaleX(0.5);
}

.rightBorder(@color: @border-color) {
  content: " ";
  position: absolute;
  right: 0;
  top: 0;
  width: 1px;
  bottom: 0;
  border-right: 1px solid @color;
  color: @color;
  transform-origin: 100% 0;
  transform: scaleX(0.5);
}

方案2: 用JS计算rem基准值和viewport缩放值。

/* 设计稿是750,采用1:100的比例,font-size为100 * (docEl.clientWidth * dpr / 750) */

const docEl = document.documentElement;
const metaEl = document.querySelector('meta[name="viewport"]');
const dpr = window.devicePixelRatio || 1;
const rem = 100 * (docEl.clientWidth * dpr / 750);
const scale = 1 / dpr;
// 设置viewport,进行缩放,达到高清效果
metaEl.setAttribute('content', 'width=' + dpr * docEl.clientWidth + ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale + ',user-scalable=no');
// 设置data-dpr属性,留作的css hack之用,解决图片模糊问题和1px细线问题
docEl.setAttribute('data-dpr', dpr);
// 动态写入样式
docEl.style.fontSize = `${rem}px`;
  • 使用方案1虽然可以满足所有场景,且修改灵活。但是对于已使用伪类的元素要多层嵌套;
  • 使用方案2用JS根据屏幕尺寸和dpr精确地设置不同屏幕所应有的rem基准值和initial-scale缩放值,这个方案不仅可以完美解决1px细线问题,而且能够解决屏幕适配。最终选择方案2;

2. 移动端图片高清显示。

问题分析: 同一张图片在高dpr下会显示模糊

对于图片模糊问题,比较好的方案就是用多倍图片(@2x),在不同的dpr下,加载不同尺寸的图片,使位图像素点个数就可以跟物理像素点个数形成 1 : 1的比例,图片就清晰了。

可行性分析:

  • 方案1: 使用H5中<img>的srcset、size属性使浏览器根据宽、高和像素密度来加载相应的图片资源。(响应式图片)
  • 方案2: 使用css3 中 image-set() 使浏览器根据用户设备分辨率适配图像。
  • 方案3: 使用媒体查询。
  • 方案4: 通过js条件判断要加载的是几倍图。

考虑到方案1和方案2可能会有兼容性问题,方案3不具有通用性(页面中的每张图片都需要写入其不同屏幕下适配的图片url),而方案4可以封装一个根据dpr选择图片组件,最终选择了方案4

image.png