adaptive.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. var adaptive = {};
  2. (function (win, lib) {
  3. var doc = win.document;
  4. var docEl = doc.documentElement;
  5. // 设备像素比
  6. var devicePixelRatio = win.devicePixelRatio;
  7. // 我们设置的布局视口与理想视口的像素比
  8. var dpr = 1;
  9. // viewport缩放值
  10. var scale = 1;
  11. // 设置viewport
  12. function setViewport() {
  13. // 判断IOS
  14. var isIPhone = /iphone/gi.test(win.navigator.appVersion);
  15. if (lib.scaleType === 2 && isIPhone || lib.scaleType === 3) {
  16. dpr = devicePixelRatio;
  17. }
  18. // window对象上增加一个属性,提供对外的布局视口与理想视口的值
  19. win.devicePixelRatioValue = dpr;
  20. // viewport缩放值,布局视口缩放后刚好显示成理想视口的宽度,页面就不会过长或过短了
  21. scale = 1 / dpr;
  22. // 获取已有的viewport
  23. var hasMetaEl = doc.querySelector('meta[name="viewport"]');
  24. var metaStr = 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no';
  25. if (dpr === 1) {
  26. metaStr = 'width=device-width, '.concat(metaStr);
  27. }
  28. if (!isIPhone && dpr !== 1) {
  29. metaStr = metaStr.concat(', target-densitydpi=device-dpi');
  30. }
  31. // 如果有,改变之
  32. if (hasMetaEl) {
  33. hasMetaEl.setAttribute('content', metaStr);
  34. }
  35. // 如果没有,添加之
  36. else {
  37. var metaEl = doc.createElement('meta');
  38. metaEl.setAttribute('name', 'viewport');
  39. metaEl.setAttribute('content', metaStr);
  40. if (docEl.firstElementChild) {
  41. docEl.firstElementChild.appendChild(metaEl);
  42. }
  43. else {
  44. var containDiv = doc.createElement('div');
  45. containDiv.appendChild(metaEl);
  46. docEl.appendChild(containDiv);
  47. }
  48. }
  49. }
  50. var newBase = 100;
  51. lib.errDpr = 1;
  52. function setRem() {
  53. // 布局视口
  54. // var layoutView = docEl.clientWidth; 也可以 获取布局视口的宽度
  55. var layoutView;
  56. if (lib.maxWidth) {
  57. layoutView = Math.min(docEl.getBoundingClientRect().width, lib.maxWidth * dpr);
  58. }
  59. else {
  60. layoutView = docEl.getBoundingClientRect().width;
  61. }
  62. // 为了计算方便,我们规定 1rem === 100px设计图像素,我们切图的时候就能快速转换
  63. // 有人问,为什么不让1rem === 1px设计像素呢?
  64. // 设计图一般是640或者750px
  65. // 布局视口一般是320到1440
  66. // 计算一个值,使layout的总宽度为 (desinWidth/100) rem
  67. // 那么有计算公式:layoutView / newBase = desinWidth / 100
  68. // newBase = 100 * layoutView / desinWidth
  69. // newBase = 介于50到200之间
  70. // 如果 1rem === 1px 设计像素,newBase就介于0.5到2之间,由于很多浏览器有最小12px限制,这个时候就不能自适应了
  71. newBase = 100 * layoutView / lib.desinWidth * (lib.errDpr || 1);
  72. docEl.style.fontSize = newBase + 'px';
  73. // rem基准值改变后,手动reflow一下,避免旋转手机后页面自适应问题
  74. doc.body&&(doc.body.style.fontSize = lib.baseFont / 100 + 'rem');
  75. // 重新设置rem后的回调方法
  76. lib.setRemCallback&&lib.setRemCallback();
  77. lib.newBase = newBase;
  78. }
  79. var tid;
  80. lib.desinWidth = 750;
  81. lib.baseFont = 28;
  82. // 局部刷新的时候部分chrome版本字体过大的问题
  83. lib.reflow = function() {
  84. docEl.clientWidth;
  85. };
  86. // 检查安卓下rem值是否显示正确
  87. function checkRem() {
  88. if (/android/ig.test(win.navigator.appVersion)) {
  89. var hideDiv = document.createElement('p');
  90. hideDiv.style.height = '1px';
  91. hideDiv.style.width = '2.5rem';
  92. hideDiv.style.visibility = 'hidden';
  93. document.body.appendChild(hideDiv);
  94. var now = hideDiv.offsetWidth;
  95. var right = lib.newBase * 2.5;
  96. if (Math.abs(right / now - 1) > 0.05) {
  97. lib.errDpr = right / now;
  98. setRem();
  99. }
  100. document.body.removeChild(hideDiv);
  101. }
  102. }
  103. lib.init = function () {
  104. // resize的时候重新设置rem基准值
  105. // 触发orientationchange 事件时也会触发resize,故不需要再添加此事件了
  106. win.addEventListener('resize', function () {
  107. clearTimeout(tid);
  108. tid = setTimeout(setRem, 300);
  109. }, false);
  110. // 浏览器缓存中读取时也需要重新设置rem基准值
  111. win.addEventListener('pageshow', function (e) {
  112. if (e.persisted) {
  113. clearTimeout(tid);
  114. tid = setTimeout(setRem, 300);
  115. }
  116. }, false);
  117. // 设置body上的字体大小
  118. if (doc.readyState === 'complete') {
  119. doc.body.style.fontSize = lib.baseFont / 100 + 'rem';
  120. checkRem();
  121. }
  122. else {
  123. doc.addEventListener('DOMContentLoaded', function (e) {
  124. doc.body.style.fontSize = lib.baseFont / 100 + 'rem';
  125. checkRem();
  126. }, false);
  127. }
  128. setViewport();
  129. // 设置rem值
  130. setRem();
  131. // html节点设置布局视口与理想视口的像素比
  132. docEl.setAttribute('data-dpr', dpr);
  133. };
  134. // 有些html元素只能以px为单位,所以需要提供一个接口,把rem单位换算成px
  135. lib.remToPx = function (remValue) {
  136. return remValue * newBase;
  137. };
  138. })(window, adaptive);
  139. if (typeof module != 'undefined' && module.exports) {
  140. module.exports = adaptive;
  141. } else if (typeof define == 'function' && define.amd) {
  142. define(function() {
  143. return adaptive;
  144. });
  145. } else {
  146. window.adaptive = adaptive;
  147. }