rem.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. (function (win, lib) {
  2. var doc = win.document;
  3. var docEl = doc.documentElement;
  4. // 设备像素比
  5. var devicePixelRatio = win.devicePixelRatio;
  6. // 我们设置的布局视口与理想视口的像素比
  7. var dpr = 1;
  8. // viewport缩放值
  9. var scale = 1;
  10. // 设置viewport
  11. function setViewport() {
  12. // 判断IOS
  13. var isIPhone = /iphone/gi.test(win.navigator.appVersion);
  14. // viewport 不缩放
  15. dpr = 1;
  16. // window对象上增加一个属性,提供对外的布局视口与理想视口的值
  17. win.devicePixelRatioValue = dpr;
  18. // viewport缩放值,布局视口缩放后刚好显示成理想视口的宽度,页面就不会过长或过短了
  19. scale = 1 / dpr;
  20. // 获取已有的viewport
  21. var hasMetaEl = doc.querySelector('meta[name="viewport"]');
  22. // 如果有,改变之
  23. if (hasMetaEl) {
  24. // ios9 不用设置 maximum-scale minimum-scale,否则页面会出现可左右拖动的效果,IOS9的bug或者故意为之?
  25. if (isIPhone) {
  26. hasMetaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no');
  27. }
  28. // target-densitydpi 目标设备密度等级,默认值medium-dpi,我们的目标是css中的1px会等于物理像素中的1px,故使用target-densitydpi=device-dpi
  29. else {
  30. hasMetaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  31. }
  32. }
  33. // 如果没有,添加之
  34. else {
  35. var metaEl = doc.createElement('meta');
  36. metaEl.setAttribute('name', 'viewport');
  37. if (isIPhone) {
  38. metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no');
  39. }
  40. else {
  41. metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
  42. }
  43. if (docEl.firstElementChild) {
  44. docEl.firstElementChild.appendChild(metaEl);
  45. }
  46. else {
  47. var wrap = doc.createElement('div');
  48. wrap.appendChild(metaEl);
  49. doc.write(wrap.innerHTML);
  50. }
  51. }
  52. }
  53. setViewport();
  54. var newBase = 100;
  55. function setRem() {
  56. // 布局视口
  57. // var layoutView = docEl.documentElement.clientWidth; 也可以 获取布局视口的宽度
  58. var layoutView;
  59. if (lib.maxWidth) {
  60. layoutView = Math.min(docEl.getBoundingClientRect().width, lib.maxWidth * dpr);
  61. }
  62. else {
  63. layoutView = docEl.getBoundingClientRect().width;
  64. }
  65. // 为了计算方便,我们规定 1rem === 100px设计图像素,我们切图的时候就能快速转换
  66. // 有人问,为什么不让1rem === 1px设计像素呢?
  67. // 设计图一般是640或者750px
  68. // 布局视口一般是320到1440
  69. // 计算一个值,使layout的总宽度为 (desinWidth/100) rem
  70. // 那么有计算公式:layoutView / newBase = desinWidth / 100
  71. // newBase = 100 * layoutView / desinWidth
  72. // newBase = 介于50到200之间
  73. // 如果 1rem === 1px 设计像素,newBase就介于0.5到2之间,由于很多浏览器有最小12px限制,这个时候就不能自适应了
  74. newBase = 100 * layoutView / lib.desinWidth;
  75. docEl.style.fontSize = newBase + 'px';
  76. // rem基准值改变后,手动reflow一下,避免旋转手机后页面自适应问题
  77. doc.body && (doc.body.style.fontSize = lib.baseFont * dpr + 'px');
  78. // 重新设置rem后的回调方法
  79. lib.setRemCallback && lib.setRemCallback();
  80. }
  81. var tid;
  82. lib.desinWidth = 640;
  83. lib.baseFont = 18;
  84. lib.init = function () {
  85. // resize的时候重新设置rem基准值
  86. // 触发orientationchange 事件时也会触发resize,故不需要再添加此事件了
  87. win.addEventListener('resize', function () {
  88. clearTimeout(tid);
  89. tid = setTimeout(setRem, 300);
  90. }, false);
  91. // 浏览器缓存中读取时也需要重新设置rem基准值
  92. win.addEventListener('pageshow', function (e) {
  93. if (e.persisted) {
  94. clearTimeout(tid);
  95. tid = setTimeout(setRem, 300);
  96. }
  97. }, false);
  98. // 设置body上的字体大小
  99. if (doc.readyState === 'complete') {
  100. doc.body.style.fontSize = lib.baseFont * dpr + 'px';
  101. }
  102. else {
  103. doc.addEventListener('DOMContentLoaded', function (e) {
  104. doc.body.style.fontSize = lib.baseFont * dpr + 'px';
  105. }, false);
  106. }
  107. // 设置rem值
  108. setRem();
  109. // html节点设置布局视口与理想视口的像素比
  110. docEl.setAttribute('data-dpr', dpr);
  111. };
  112. // 有些html元素只能以px为单位,所以需要提供一个接口,把rem单位换算成px
  113. lib.remToPx = function (remValue) {
  114. return remValue * newBase;
  115. };
  116. })(window, window['adaptive'] || (window['adaptive'] = {}));