mUtils.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /**
  2. * 存储localStorage
  3. */
  4. export const setStore = (name, content) => {
  5. if (!name) return;
  6. if (typeof content !== 'string') {
  7. content = JSON.stringify(content);
  8. }
  9. window.localStorage.setItem(name, content);
  10. }
  11. /**
  12. * 获取localStorage
  13. */
  14. export const getStore = name => {
  15. if (!name) return;
  16. return window.localStorage.getItem(name);
  17. }
  18. /**
  19. * 删除localStorage
  20. */
  21. export const removeStore = name => {
  22. if (!name) return;
  23. window.localStorage.removeItem(name);
  24. }
  25. /**
  26. * 获取style样式
  27. */
  28. export const getStyle = (element, attr, NumberMode = 'int') => {
  29. let target;
  30. // scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用
  31. if (attr === 'scrollTop') {
  32. target = element.scrollTop;
  33. }else if(element.currentStyle){
  34. target = element.currentStyle[attr];
  35. }else{
  36. target = document.defaultView.getComputedStyle(element,null)[attr];
  37. }
  38. //在获取 opactiy 时需要获取小数 parseFloat
  39. return NumberMode == 'float'? parseFloat(target) : parseInt(target);
  40. }
  41. /**
  42. * 页面到达底部,加载更多
  43. */
  44. export const loadMore = (element, callback) => {
  45. let windowHeight = window.screen.height;
  46. let height;
  47. let setTop;
  48. let paddingBottom;
  49. let marginBottom;
  50. let requestFram;
  51. let oldScrollTop;
  52. document.body.addEventListener('scroll',() => {
  53. loadMore();
  54. }, false)
  55. //运动开始时获取元素 高度 和 offseTop, pading, margin
  56. element.addEventListener('touchstart',() => {
  57. height = element.offsetHeight;
  58. setTop = element.offsetTop;
  59. paddingBottom = getStyle(element,'paddingBottom');
  60. marginBottom = getStyle(element,'marginBottom');
  61. },{passive: true})
  62. //运动过程中保持监听 scrollTop 的值判断是否到达底部
  63. element.addEventListener('touchmove',() => {
  64. loadMore();
  65. },{passive: true})
  66. //运动结束时判断是否有惯性运动,惯性运动结束判断是非到达底部
  67. element.addEventListener('touchend',() => {
  68. oldScrollTop = document.body.scrollTop;
  69. moveEnd();
  70. },{passive: true})
  71. const moveEnd = () => {
  72. requestFram = requestAnimationFrame(() => {
  73. if (document.body.scrollTop != oldScrollTop) {
  74. oldScrollTop = document.body.scrollTop;
  75. loadMore();
  76. moveEnd();
  77. }else{
  78. cancelAnimationFrame(requestFram);
  79. //为了防止鼠标抬起时已经渲染好数据从而导致重获取数据,应该重新获取dom高度
  80. height = element.offsetHeight;
  81. loadMore();
  82. }
  83. })
  84. }
  85. const loadMore = () => {
  86. if (document.body.scrollTop + windowHeight >= height + setTop + paddingBottom + marginBottom) {
  87. callback();
  88. }
  89. }
  90. }
  91. /**
  92. * 显示返回顶部按钮,开始、结束、运动 三个过程中调用函数判断是否达到目标点
  93. */
  94. export const showBack = callback => {
  95. let requestFram;
  96. let oldScrollTop;
  97. document.addEventListener('scroll',() => {
  98. showBackFun();
  99. }, false)
  100. document.addEventListener('touchstart',() => {
  101. showBackFun();
  102. },{passive: true})
  103. document.addEventListener('touchmove',() => {
  104. showBackFun();
  105. },{passive: true})
  106. document.addEventListener('touchend',() => {
  107. oldScrollTop = document.body.scrollTop;
  108. moveEnd();
  109. },{passive: true})
  110. const moveEnd = () => {
  111. requestFram = requestAnimationFrame(() => {
  112. if (document.body.scrollTop != oldScrollTop) {
  113. oldScrollTop = document.body.scrollTop;
  114. moveEnd();
  115. }else{
  116. cancelAnimationFrame(requestFram);
  117. }
  118. showBackFun();
  119. })
  120. }
  121. //判断是否达到目标点
  122. const showBackFun = () => {
  123. if (document.body.scrollTop > 500) {
  124. callback(true);
  125. }else{
  126. callback(false);
  127. }
  128. }
  129. }
  130. /**
  131. * 运动效果
  132. * @param {HTMLElement} element 运动对象,必选
  133. * @param {JSON} target 属性:目标值,必选
  134. * @param {number} duration 运动时间,可选
  135. * @param {string} mode 运动模式,可选
  136. * @param {function} callback 可选,回调函数,链式动画
  137. */
  138. export const animate = (element, target, duration = 400, mode = 'ease-out', callback) => {
  139. clearInterval(element.timer);
  140. //判断不同参数的情况
  141. if (duration instanceof Function) {
  142. callback = duration;
  143. duration = 400;
  144. }else if(duration instanceof String){
  145. mode = duration;
  146. duration = 400;
  147. }
  148. //判断不同参数的情况
  149. if (mode instanceof Function) {
  150. callback = mode;
  151. mode = 'ease-out';
  152. }
  153. //获取dom样式
  154. const attrStyle = attr => {
  155. if (attr === "opacity") {
  156. return Math.round(getStyle(element, attr, 'float') * 100);
  157. } else {
  158. return getStyle(element, attr);
  159. }
  160. }
  161. //根字体大小,需要从此将 rem 改成 px 进行运算
  162. const rootSize = parseFloat(document.documentElement.style.fontSize);
  163. const unit = {};
  164. const initState = {};
  165. //获取目标属性单位和初始样式值
  166. Object.keys(target).forEach(attr => {
  167. if (/[^\d^\.]+/gi.test(target[attr])) {
  168. unit[attr] = target[attr].match(/[^\d^\.]+/gi)[0] || 'px';
  169. }else{
  170. unit[attr] = 'px';
  171. }
  172. initState[attr] = attrStyle(attr);
  173. });
  174. //去掉传入的后缀单位
  175. Object.keys(target).forEach(attr => {
  176. if (unit[attr] == 'rem') {
  177. target[attr] = Math.ceil(parseInt(target[attr])*rootSize);
  178. }else{
  179. target[attr] = parseInt(target[attr]);
  180. }
  181. });
  182. let flag = true; //假设所有运动到达终点
  183. const remberSpeed = {};//记录上一个速度值,在ease-in模式下需要用到
  184. element.timer = setInterval(() => {
  185. Object.keys(target).forEach(attr => {
  186. let iSpeed = 0; //步长
  187. let status = false; //是否仍需运动
  188. let iCurrent = attrStyle(attr) || 0; //当前元素属性址
  189. let speedBase = 0; //目标点需要减去的基础值,三种运动状态的值都不同
  190. let intervalTime; //将目标值分为多少步执行,数值越大,步长越小,运动时间越长
  191. switch(mode){
  192. case 'ease-out':
  193. speedBase = iCurrent;
  194. intervalTime = duration*5/400;
  195. break;
  196. case 'linear':
  197. speedBase = initState[attr];
  198. intervalTime = duration*20/400;
  199. break;
  200. case 'ease-in':
  201. let oldspeed = remberSpeed[attr] || 0;
  202. iSpeed = oldspeed + (target[attr] - initState[attr])/duration;
  203. remberSpeed[attr] = iSpeed
  204. break;
  205. default:
  206. speedBase = iCurrent;
  207. intervalTime = duration*5/400;
  208. }
  209. if (mode !== 'ease-in') {
  210. iSpeed = (target[attr] - speedBase) / intervalTime;
  211. iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
  212. }
  213. //判断是否达步长之内的误差距离,如果到达说明到达目标点
  214. switch(mode){
  215. case 'ease-out':
  216. status = iCurrent != target[attr];
  217. break;
  218. case 'linear':
  219. status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed);
  220. break;
  221. case 'ease-in':
  222. status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed);
  223. break;
  224. default:
  225. status = iCurrent != target[attr];
  226. }
  227. if (status) {
  228. flag = false;
  229. //opacity 和 scrollTop 需要特殊处理
  230. if (attr === "opacity") {
  231. element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")";
  232. element.style.opacity = (iCurrent + iSpeed) / 100;
  233. } else if (attr === 'scrollTop') {
  234. element.scrollTop = iCurrent + iSpeed;
  235. }else{
  236. element.style[attr] = iCurrent + iSpeed + 'px';
  237. }
  238. } else {
  239. flag = true;
  240. }
  241. if (flag) {
  242. clearInterval(element.timer);
  243. if (callback) {
  244. callback();
  245. }
  246. }
  247. })
  248. }, 20);
  249. }