config.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import {Component, Mixins, Ref} from "vue-property-decorator";
  2. import {emitNames} from '../enum/emit';
  3. import ScrollApi from '../api/scroll';
  4. import ScrollStatus from './status';
  5. import {
  6. Config,
  7. EventTypes,
  8. ListenerEventTarget,
  9. ScrollConfig,
  10. ScrollXConfig,
  11. ScrollYConfig
  12. } from "../types/on";
  13. @Component({
  14. created(this:ScrollViewConfig){
  15. this._config = {};
  16. this._scrollConfig = {
  17. look:false,
  18. stipitate:undefined,
  19. x:{
  20. x:0,
  21. width:0,
  22. actualWidth:0,
  23. maxScroll:0
  24. },
  25. y:{
  26. y:0,
  27. height:0,
  28. actualHeight:0,
  29. maxScroll:0
  30. }
  31. };
  32. },
  33. mounted(this:ScrollViewConfig){
  34. // 执行安装
  35. return this.installScroll();
  36. },
  37. // 释放组件的时候触发
  38. beforeDestroy(this:ScrollViewConfig){
  39. this._scrollApi && this._scrollApi.destroy();
  40. this._scrollConfig.stipitate&&clearTimeout(this._scrollConfig.stipitate);
  41. this._scrollApi = undefined;
  42. this.scrollEl && this._config.scrollUnique && this.removeEventListener(this.scrollEl,'scroll',this._config.scrollUnique);
  43. }
  44. })
  45. export default class ScrollViewConfig extends Mixins(ScrollStatus) {
  46. // 滚动标签实例
  47. @Ref('scroll') scrollEl:HTMLElement | undefined;
  48. // 滚动包容标签实例
  49. @Ref('scrollTake') scrollTakeEl:HTMLElement | undefined;
  50. // 运行时的 scrollApi
  51. _scrollApi:ScrollApi | undefined;
  52. // 运行时参数
  53. _config:Config={};
  54. // 滚动系列参数配置
  55. _scrollConfig:ScrollConfig={
  56. look:false,
  57. stipitate:undefined,
  58. x:{
  59. x:0,
  60. width:0,
  61. actualWidth:0,
  62. maxScroll:0
  63. },
  64. y:{
  65. y:0,
  66. height:0,
  67. actualHeight:0,
  68. maxScroll:0
  69. }
  70. };
  71. // 配置滚动系列参数
  72. setScrollConfig(){
  73. if (this._scrollConfig.look) return;
  74. // 设置锁
  75. this._scrollConfig.look = true;
  76. // 配置 x 轴
  77. this.scrollX && this.setScrollAxisConfig('x');
  78. // 配置 y 轴
  79. this.scrollY && this.setScrollAxisConfig('y');
  80. // 帧频率不允许重复
  81. this._scrollConfig.stipitate = setTimeout(()=>{
  82. this._scrollConfig.stipitate = undefined;
  83. this._scrollConfig.look = false;
  84. },16.66);
  85. };
  86. // 配置单独轴系列参数
  87. setScrollAxisConfig(axis:'x' | 'y'){
  88. if (this.scrollEl === undefined || this.scrollTakeEl === undefined) return;
  89. let sizeKey: 'width' | 'height' = axis === 'x' ? 'width':'height';
  90. let actualKey : 'actualWidth' | 'actualHeight' = axis === 'x' ? 'actualWidth' :'actualHeight';
  91. let scrollAxis: 'scrollLeft' | 'scrollTop' = axis === 'x'? 'scrollLeft' :'scrollTop';
  92. let elSizeKey:'offsetWidth' | 'offsetHeight' = axis === 'x' ? 'offsetWidth' :'offsetHeight';
  93. // 设置配置文件
  94. // @ts-ignore
  95. this._scrollConfig[axis] = {
  96. [sizeKey]: this.scrollEl[elSizeKey] || 0,
  97. [axis]: this.scrollEl[scrollAxis] || 0,
  98. [actualKey]: this.scrollTakeEl[elSizeKey] || 0,
  99. maxScroll:0
  100. };
  101. //
  102. // // 设置最大滚动
  103. // @ts-ignore
  104. (this._scrollConfig[axis] as ScrollYConfig | ScrollXConfig).maxScroll = Math.max(0,this._scrollConfig[axis][actualKey] - this._scrollConfig[axis][sizeKey]);
  105. };
  106. // 安装
  107. installScroll(){
  108. // 设置当前的配置文件
  109. this.setScrollConfig();
  110. // 创建滚动Api
  111. if (this.scrollEl !== undefined) {
  112. this._scrollApi = new ScrollApi(this._scrollConfig,this.scrollEl);
  113. }
  114. // 如果存在此三种的任何一种 事件开启监听
  115. if (
  116. (this.$listeners[emitNames.onScroll]
  117. ||
  118. this.$listeners[emitNames.onScrollLower]
  119. ||
  120. this.$listeners[emitNames.onScrollUpper]) && this.scrollEl !== undefined
  121. ) {
  122. // 获取存储的唯一标识
  123. this._config.scrollUnique = this.addEventListener(this.scrollEl,'scroll',()=> this.onScroll());
  124. }
  125. }
  126. // 滚动时触发
  127. onScroll(){
  128. if (this.scrollEl) {
  129. // 设置配置
  130. this.setScrollConfig();
  131. if (this.$listeners[emitNames.onScrollUpper] || this.$listeners[emitNames.onScrollLower]) {
  132. if (this.scrollY) {
  133. if ( this.$listeners[emitNames.onScrollLower] && this.testScrollDistance('y','lowerThreshold','_lowerThresholdYStatus')) {
  134. this.emitScrollLower('y');
  135. } else if (this.$listeners[emitNames.onScrollUpper] && this.testScrollDistance('y','upperThreshold','_upperThresholdYStatus')) {
  136. this.emitScrollUpper('y');
  137. }
  138. }
  139. if (this.scrollX) {
  140. if ( this.$listeners[emitNames.onScrollLower] && this.testScrollDistance('x','lowerThreshold','_lowerThresholdXStatus')) {
  141. this.emitScrollLower('x');
  142. } else if (this.$listeners[emitNames.onScrollUpper] && this.testScrollDistance('x','upperThreshold','_upperThresholdXStatus')) {
  143. this.emitScrollUpper('x');
  144. }
  145. }
  146. }
  147. if(this.$listeners[emitNames.onScroll]) {
  148. return this.emitScroll(this._scrollConfig);
  149. }
  150. }
  151. }
  152. // 校验是否满足条件
  153. testScrollDistance(axis:'x' | 'y',key:'lowerThreshold' | 'upperThreshold',testKey:'_lowerThresholdXStatus'|'_lowerThresholdYStatus'|'_upperThresholdXStatus'|'_upperThresholdYStatus'){
  154. if (
  155. this[key] !== undefined
  156. &&
  157. // @ts-ignore
  158. key === 'upperThreshold' ? this._scrollConfig[axis][axis] <= this[key] : this._scrollConfig[axis].maxScroll - this._scrollConfig[axis][axis] <= this[key]
  159. ) {
  160. if (!this[testKey]) {
  161. this[testKey] = true;
  162. return true;
  163. }
  164. } else {
  165. this[testKey] = false;
  166. }
  167. return false;
  168. }
  169. // 监听器对象
  170. _listeners: { [key:string]:ListenerEventTarget } | undefined = undefined;
  171. // 添加监听
  172. addEventListener<T extends EventTypes>(el:HTMLElement,event:T,target:ListenerEventTarget){
  173. // 如果没有监听器对象创建
  174. if (this._listeners === undefined) {
  175. this._listeners = {};
  176. }
  177. let unique:string = this.getUnique(event);
  178. this._listeners[unique] = target;
  179. if (window.addEventListener !== undefined) {
  180. el.addEventListener(event,target,{
  181. passive:true
  182. });
  183. } else {
  184. // @ts-ignore 兼容策略
  185. el.attachEvent('on'+event,target,{
  186. passive:true
  187. });
  188. }
  189. return unique;
  190. }
  191. // 移除兼容
  192. removeEventListener<K extends EventTypes>(el:HTMLElement,event:K,unique:string){
  193. if (this._listeners !== undefined && this._listeners[unique] !== undefined) {
  194. if (window.removeEventListener !== undefined) {
  195. el.removeEventListener(event, this._listeners[unique]);
  196. } else {
  197. // @ts-ignore 兼容策略
  198. el.detachEvent('on' + event, this._listeners[unique]);
  199. }
  200. return true;
  201. } else {
  202. return false;
  203. }
  204. }
  205. // 获取唯一值
  206. getUnique(value:string=''):string{
  207. return new Date().getTime() + value + Math.floor(Math.random() * 10000) ;
  208. }
  209. }