├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── build
├── seamscroll.js
└── seamscroll.min.js
├── document
└── README.md
├── index.html
├── package.json
├── script
├── build-debug.js
└── build.js
├── src
├── content
│ ├── create.js
│ ├── event.js
│ ├── getCss.js
│ └── seamless.js
└── index.js
└── test
├── demo.css
├── reset.css
└── timg.jpg
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "targets": {
7 | "browsers": [
8 | "last 2 versions",
9 | "safari 7",
10 | "not ie <= 9"
11 | ]
12 | },
13 | "modules": false,
14 | "loose": true
15 | }
16 | ]
17 | ]
18 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # files
2 | *.log
3 | *.zip
4 | .DS_Store
5 | package-lock.json
6 |
7 | # folders
8 | node_modules/*
9 |
10 | # Editor directories and files
11 | .idea
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 chenxuan1993
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # seamless-scroll
2 | > js seamless-scroll plugin
3 |
4 | [ ](https://www.npmjs.com/package/seamscroll) 
5 |
6 |
7 |
8 |
9 |
10 |
11 | 🌾 sample demo |
12 | 📘 中文文档
13 |
14 |
15 |
16 | ## Browser support
17 | | [ ](http://godban.github.io/browsers-support-badges/)IE | [ ](http://godban.github.io/browsers-support-badges/)Firefox | [ ](http://godban.github.io/browsers-support-badges/)Chrome | [ ](http://godban.github.io/browsers-support-badges/)Safari | [ ](http://godban.github.io/browsers-support-badges/)iOS | [ ](http://godban.github.io/browsers-support-badges/)Android |
18 | |:---------:|:---------:|:---------:|:---------:|:---------:|:---------:|
19 | | IE7+ | ✓| ✓ | ✓ | ✓ | ✓ | ✓
20 |
21 | * mobile gestures are not supported.
22 |
23 |
24 | ## Installation
25 |
26 | ### NPM
27 |
28 | ```bash
29 | npm install seamscroll --save
30 | ```
31 | ### CDN
32 | `https://cdn.jsdelivr.net/npm/seamscroll@0.0.11/build/seamscroll.min.js`
33 |
34 | ## Usage
35 |
36 | ```js
37 | const seamless = require('seamscroll')
38 | `or`
39 | import seamless from 'seamscroll'
40 |
41 | seamless.init({
42 | dom: document.getElementById('demo1')
43 | })
44 |
45 | //script tag
46 |
47 |
52 | ```
53 |
54 | ## Demo
55 | ```css
56 | .demo2 {
57 | width: 600px;
58 | height: 100px;
59 | position: relative;
60 | overflow: hidden;
61 | margin-top: 100px;
62 | }
63 | .list2 li {
64 | float: left;
65 | width: 100px;
66 | height: 100px;
67 | margin-right: 20px;
68 | text-align: center;
69 | font-size: 20px;
70 | color: #fff;
71 | line-height:100px;
72 | background-color: #ccc;
73 | }
74 | ```
75 | ```html
76 |
77 |
78 | 1
79 | 2
80 | 3
81 | 4
82 | 5
83 | 6
84 |
85 |
86 | ```
87 | ```javascript
88 |
89 | seamscroll.init({
90 | dom: document.getElementById('demo2'),
91 | direction: 2
92 | })
93 | ```
94 |
95 | ## Configure
96 | *Required parameters(dom)
97 |
98 | |key|description|default|val|
99 | |:---|---|---|---|
100 | |`*dom`|the role of the element|`null`|`dom`|
101 | |`step`|step,the faster the rolling speed is faster|`1`|`Number`|
102 | |`hoverStop`|mouse hover control is enabled|`true`|`Boolean`|
103 | |`direction`|0 down 1 up 2 left 3 right|`1`|`Number`|
104 | |`singleHeight`|one single stop height(default zero is seamless) => direction 0/1|`0`|`Number`|
105 | |`singleWidth`|one single stop width(default zero is seamless) => direction 2/3|`0`|`Number`|
106 | |`waitTime`|one single data stop wait time(1s)|`1000`|`Number`|
107 |
108 | ## Changelog
109 | See the GitHub [release history](https://github.com/chenxuan0000/seamless-scroll/releases).
110 |
111 | ## License
112 | seamless-scroll is open source and released under the [MIT License](LICENSE).
--------------------------------------------------------------------------------
/build/seamscroll.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory();
4 | else if(typeof define === 'function' && define.amd)
5 | define([], factory);
6 | else if(typeof exports === 'object')
7 | exports["seamscroll"] = factory();
8 | else
9 | root["seamscroll"] = factory();
10 | })(typeof self !== 'undefined' ? self : this, function() {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId]) {
20 | /******/ return installedModules[moduleId].exports;
21 | /******/ }
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ i: moduleId,
25 | /******/ l: false,
26 | /******/ exports: {}
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.l = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // define getter function for harmony exports
47 | /******/ __webpack_require__.d = function(exports, name, getter) {
48 | /******/ if(!__webpack_require__.o(exports, name)) {
49 | /******/ Object.defineProperty(exports, name, {
50 | /******/ configurable: false,
51 | /******/ enumerable: true,
52 | /******/ get: getter
53 | /******/ });
54 | /******/ }
55 | /******/ };
56 | /******/
57 | /******/ // getDefaultExport function for compatibility with non-harmony modules
58 | /******/ __webpack_require__.n = function(module) {
59 | /******/ var getter = module && module.__esModule ?
60 | /******/ function getDefault() { return module['default']; } :
61 | /******/ function getModuleExports() { return module; };
62 | /******/ __webpack_require__.d(getter, 'a', getter);
63 | /******/ return getter;
64 | /******/ };
65 | /******/
66 | /******/ // Object.prototype.hasOwnProperty.call
67 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
68 | /******/
69 | /******/ // __webpack_public_path__
70 | /******/ __webpack_require__.p = "";
71 | /******/
72 | /******/ // Load entry module and return exports
73 | /******/ return __webpack_require__(__webpack_require__.s = 0);
74 | /******/ })
75 | /************************************************************************/
76 | /******/ ([
77 | /* 0 */
78 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
79 |
80 | "use strict";
81 | Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
82 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__content_create__ = __webpack_require__(1);
83 | /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "init", function() { return __WEBPACK_IMPORTED_MODULE_0__content_create__["a"]; });
84 | /**
85 | * @desc webpack打包入口
86 | */
87 |
88 |
89 |
90 |
91 |
92 | /***/ }),
93 | /* 1 */
94 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
95 |
96 | "use strict";
97 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__seamless__ = __webpack_require__(2);
98 |
99 |
100 | var create = function create(options) {
101 | return new __WEBPACK_IMPORTED_MODULE_0__seamless__["a" /* default */](options);
102 | };
103 |
104 | /* harmony default export */ __webpack_exports__["a"] = (create);
105 |
106 | /***/ }),
107 | /* 2 */
108 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
109 |
110 | "use strict";
111 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__event__ = __webpack_require__(5);
112 | /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__getCss__ = __webpack_require__(6);
113 | __webpack_require__(3)();
114 | var copyObj = __webpack_require__(4);
115 |
116 |
117 |
118 | var defaultOptions = {
119 | step: 1, //步长
120 | hoverStop: true, //是否启用鼠标hover控制
121 | direction: 1, // 0 往下 1 往上 2向左 3向右
122 | singleHeight: 0, //单条数据高度有值hoverStop关闭
123 | singleWidth: 0, //单条数据宽度有值hoverStop关闭
124 | waitTime: 1000 //单步停止等待时间
125 | };
126 |
127 | var seamless = function seamless(options) {
128 | this.options = copyObj({}, defaultOptions, options);
129 | var dom = this.options.dom;
130 | if (!dom) throw new Error('you must set a dom');
131 | dom.style.position = 'relative';
132 | dom.style.overflow = 'hidden';
133 | this.reqFrame = null;
134 | this.singleWaitTime = null; // single 单步滚动的定时器
135 | this._top = 0;
136 | this._left = 0;
137 | this.isHover = false; //_move()方法的开关
138 | dom.innerHTML += dom.innerHTML;
139 | if (this.options.direction > 1) {
140 | //水平向滚动
141 | var child = dom.children,
142 | childFirst = child[0],
143 | len = child.length;
144 | this._width = (childFirst.offsetWidth + this._getInt(childFirst, 'margin-left') + this._getInt(childFirst, 'margin-right')) * len;
145 | dom.style.width = this._width + 'px';
146 | }
147 | this._move();
148 | this._bindEvent();
149 | };
150 |
151 | seamless.prototype = {
152 | _getInt: function _getInt(dom, name) {
153 | return parseInt(Object(__WEBPACK_IMPORTED_MODULE_1__getCss__["a" /* default */])(dom, name));
154 | },
155 | _cancle: function _cancle() {
156 | cancelAnimationFrame(this.reqFrame || '');
157 | },
158 | _bindEvent: function _bindEvent() {
159 | if (!this.options.hoverStop) return;
160 | var that = this;
161 | var dom = this.options.dom;
162 | Object(__WEBPACK_IMPORTED_MODULE_0__event__["a" /* default */])(dom, 'mouseenter', function () {
163 | that.isHover = true; // 关闭_move
164 | // 防止蛋疼的人频频hover进出单步滚动 导致定时器乱掉
165 | if (that.singleWaitTime) clearTimeout(that.singleWaitTime);
166 | that._cancle();
167 | });
168 | Object(__WEBPACK_IMPORTED_MODULE_0__event__["a" /* default */])(dom, 'mouseleave', function () {
169 | that.isHover = false; // 开启_move
170 | that._move();
171 | });
172 | },
173 | _move: function _move() {
174 | if (this.isHover) return;
175 | this._cancle();
176 | var that = this;
177 | var dom = this.options.dom;
178 | this.reqFrame = requestAnimationFrame(function () {
179 | var h = dom.offsetHeight / 2; //实际高度
180 | var direction = that.options.direction; //滚动方向
181 | if (direction === 1) {
182 | // 上
183 | if (Math.abs(that._top) >= h) that._top = 0;
184 | that._top -= that.options.step;
185 | dom.style.top = that._top + 'px';
186 | } else if (direction === 0) {
187 | // 下
188 | if (that._top >= 0) that._top = h * -1;
189 | that._top += that.options.step;
190 | dom.style.top = that._top + 'px';
191 | } else if (direction === 2) {
192 | // 左
193 | if (Math.abs(that._left) >= that._width / 2) that._left = 0;
194 | that._left -= that.options.step;
195 | dom.style.left = that._left + 'px';
196 | } else if (direction === 3) {
197 | // 右
198 | if (that._left >= 0) that._left = that._width / 2 * -1;
199 | that._left += that.options.step;
200 | dom.style.left = that._left + 'px';
201 | }
202 | that._judgeSingle();
203 | });
204 | },
205 | _judgeSingle: function _judgeSingle() {
206 | var _this = this;
207 |
208 | var singleH = this.options.singleHeight;
209 | var singleW = this.options.singleWidth;
210 | if (this.singleWaitTime) clearTimeout(this.singleWaitTime);
211 | if (!!singleH) {
212 | //是否启动了单行暂停配置
213 | if (Math.abs(this._top) % singleH === 0) {
214 | // 符合条件暂停waitTime
215 | this.singleWaitTime = setTimeout(function () {
216 | _this._move();
217 | }, this.options.waitTime);
218 | } else {
219 | this._move();
220 | }
221 | } else if (!!singleW) {
222 | if (Math.abs(this._left) % singleW === 0) {
223 | // 符合条件暂停waitTime
224 | this.singleWaitTime = setTimeout(function () {
225 | _this._move();
226 | }, this.options.waitTime);
227 | } else {
228 | this._move();
229 | }
230 | } else {
231 | this._move();
232 | }
233 | }
234 | };
235 |
236 | /* harmony default export */ __webpack_exports__["a"] = (seamless);
237 |
238 | /***/ }),
239 | /* 3 */
240 | /***/ (function(module, exports) {
241 |
242 | /**
243 | * @desc AnimationFrame简单兼容hack
244 | */
245 | var animationFrame = function animationFrame() {
246 | window.cancelAnimationFrame = function () {
247 | return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame || function (id) {
248 | return window.clearTimeout(id);
249 | };
250 | }();
251 | window.requestAnimationFrame = function () {
252 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
253 | return window.setTimeout(callback, 1000 / 60);
254 | };
255 | }();
256 | };
257 |
258 | module.exports = animationFrame;
259 |
260 | /***/ }),
261 | /* 4 */
262 | /***/ (function(module, exports) {
263 |
264 | var _typeof2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
265 |
266 | var _typeof = typeof Symbol === "function" && _typeof2(Symbol.iterator) === "symbol" ? function (obj) {
267 | return typeof obj === "undefined" ? "undefined" : _typeof2(obj);
268 | } : function (obj) {
269 | return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj === "undefined" ? "undefined" : _typeof2(obj);
270 | };
271 |
272 | /**
273 | * @desc 深浅合并拷贝
274 | */
275 | function copyObj() {
276 | if (!Array.isArray) {
277 | Array.isArray = function (arg) {
278 | return Object.prototype.toString.call(arg) === '[object Array]';
279 | };
280 | }
281 | var name = void 0,
282 | options = void 0,
283 | src = void 0,
284 | copy = void 0,
285 | copyIsArray = void 0,
286 | clone = void 0,
287 | i = 1,
288 | target = arguments[0] || {},
289 |
290 | // 使用||运算符,排除隐式强制类型转换为false的数据类型
291 | deep = false,
292 | len = arguments.length;
293 | if (typeof target === 'boolean') {
294 | deep = target;
295 | target = arguments[1] || {};
296 | i++;
297 | }
298 | if ((typeof target === 'undefined' ? 'undefined' : _typeof(target)) !== 'object' && typeof target !== 'function') {
299 | target = {};
300 | }
301 | // 如果arguments.length === 1 或typeof arguments[0] === 'boolean',且存在arguments[1],则直接返回target对象
302 | if (i === len) {
303 | return target;
304 | }
305 | for (; i < len; i++) {
306 | //所以如果源对象中数据类型为Undefined或Null那么就会跳过本次循环,接着循环下一个源对象
307 | if ((options = arguments[i]) != null) {
308 | // 如果遇到源对象的数据类型为Boolean, Number for in循环会被跳过,不执行for in循环// src用于判断target对象是否存在name属性
309 | for (name in options) {
310 | // src用于判断target对象是否存在name属性
311 | src = target[name];
312 | // 需要复制的属性当前源对象的name属性
313 | copy = options[name];
314 | // 判断copy是否是数组
315 | copyIsArray = Array.isArray(copy);
316 | // 如果是深复制且copy是一个对象或数组则需要递归直到copy成为一个基本数据类型为止
317 | if (deep && copy && ((typeof copy === 'undefined' ? 'undefined' : _typeof(copy)) === 'object' || copyIsArray)) {
318 | if (copyIsArray) {
319 | copyIsArray = false;
320 | // 如果目标对象存在name属性且是一个数组
321 | // 则使用目标对象的name属性,否则重新创建一个数组,用于复制
322 | clone = src && Array.isArray(src) ? src : [];
323 | } else {
324 | // 如果目标对象存在name属性且是一个对象则使用目标对象的name属性,否则重新创建一个对象,用于复制
325 | clone = src && (typeof src === 'undefined' ? 'undefined' : _typeof(src)) === 'object' ? src : {};
326 | }
327 | // 深复制,所以递归调用copyObject函数
328 | // 返回值为target对象,即clone对象
329 | // copy是一个源对象
330 | target[name] = copyObj(deep, clone, copy);
331 | } else if (copy !== undefined) {
332 | // 浅复制,直接复制到target对象上
333 | target[name] = copy;
334 | }
335 | }
336 | }
337 | }
338 | return target;
339 | }
340 |
341 | module.exports = copyObj;
342 |
343 | /***/ }),
344 | /* 5 */
345 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
346 |
347 | "use strict";
348 | var addEventListener = function addEventListener(element, type, callback) {
349 | if (element.addEventListener) {
350 | element.addEventListener(type, callback, false);
351 | } else if (element.attachEvent) {
352 | element.attachEvent('on' + type, callback);
353 | } else {
354 | element['on' + type] = callback;
355 | }
356 | };
357 |
358 | /* harmony default export */ __webpack_exports__["a"] = (addEventListener);
359 |
360 | /***/ }),
361 | /* 6 */
362 | /***/ (function(module, __webpack_exports__, __webpack_require__) {
363 |
364 | "use strict";
365 | var getStyle = function getStyle(dom, name) {
366 | var elem = dom.currentStyle ? dom.currentStyle : window.getComputedStyle(dom, null);
367 | return elem[name];
368 | };
369 |
370 | /* harmony default export */ __webpack_exports__["a"] = (getStyle);
371 |
372 | /***/ })
373 | /******/ ]);
374 | });
--------------------------------------------------------------------------------
/build/seamscroll.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.seamscroll=e():t.seamscroll=e()}("undefined"!=typeof self?self:this,function(){return function(t){function e(i){if(n[i])return n[i].exports;var o=n[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};return e.m=t,e.c=n,e.d=function(t,n,i){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:i})},e.n=function(t){var n=t&&t.__esModule?function(){return t["default"]}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=0)}([function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var i=n(1);n.d(e,"init",function(){return i.a})},function(t,e,n){"use strict";var i=n(2),o=function(t){return new i.a(t)};e.a=o},function(t,e,n){"use strict";var i=n(5),o=n(6);n(3)();var r=n(4),s={step:1,hoverStop:!0,direction:1,singleHeight:0,singleWidth:0,waitTime:1e3},a=function(t){this.options=r({},s,t);var e=this.options.dom;if(!e)throw new Error("you must set a dom");if(e.style.position="relative",e.style.overflow="hidden",this.reqFrame=null,this.singleWaitTime=null,this._top=0,this._left=0,this.isHover=!1,e.innerHTML+=e.innerHTML,this.options.direction>1){var n=e.children,i=n[0],o=n.length;this._width=(i.offsetWidth+this._getInt(i,"margin-left")+this._getInt(i,"margin-right"))*o,e.style.width=this._width+"px"}this._move(),this._bindEvent()};a.prototype={_getInt:function(t,e){return parseInt(Object(o.a)(t,e))},_cancle:function(){cancelAnimationFrame(this.reqFrame||"")},_bindEvent:function(){if(this.options.hoverStop){var t=this,e=this.options.dom;Object(i.a)(e,"mouseenter",function(){t.isHover=!0,t.singleWaitTime&&clearTimeout(t.singleWaitTime),t._cancle()}),Object(i.a)(e,"mouseleave",function(){t.isHover=!1,t._move()})}},_move:function(){if(!this.isHover){this._cancle();var t=this,e=this.options.dom;this.reqFrame=requestAnimationFrame(function(){var n=e.offsetHeight/2,i=t.options.direction;1===i?(Math.abs(t._top)>=n&&(t._top=0),t._top-=t.options.step,e.style.top=t._top+"px"):0===i?(t._top>=0&&(t._top=-1*n),t._top+=t.options.step,e.style.top=t._top+"px"):2===i?(Math.abs(t._left)>=t._width/2&&(t._left=0),t._left-=t.options.step,e.style.left=t._left+"px"):3===i&&(t._left>=0&&(t._left=t._width/2*-1),t._left+=t.options.step,e.style.left=t._left+"px"),t._judgeSingle()})}},_judgeSingle:function(){var t=this,e=this.options.singleHeight,n=this.options.singleWidth;this.singleWaitTime&&clearTimeout(this.singleWaitTime),e?Math.abs(this._top)%e==0?this.singleWaitTime=setTimeout(function(){t._move()},this.options.waitTime):this._move():n&&Math.abs(this._left)%n==0?this.singleWaitTime=setTimeout(function(){t._move()},this.options.waitTime):this._move()}},e.a=a},function(t,e){var n=function(){window.cancelAnimationFrame=function(){return window.cancelAnimationFrame||window.webkitCancelAnimationFrame||window.mozCancelAnimationFrame||window.oCancelAnimationFrame||window.msCancelAnimationFrame||function(t){return window.clearTimeout(t)}}(),window.requestAnimationFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)}}()};t.exports=n},function(t,e){function n(){Array.isArray||(Array.isArray=function(t){return"[object Array]"===Object.prototype.toString.call(t)});var t=void 0,e=void 0,i=void 0,r=void 0,s=void 0,a=void 0,u=1,c=arguments[0]||{},f=!1,l=arguments.length;if("boolean"==typeof c&&(f=c,c=arguments[1]||{},u++),"object"!==(void 0===c?"undefined":o(c))&&"function"!=typeof c&&(c={}),u===l)return c;for(;u js无缝滚动插件
3 |
4 | [ ](https://www.npmjs.com/package/seamscroll) 
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | 🌾 简单demo |
13 | 📘 English Document
14 |
15 |
16 | ## 兼容性
17 | | [ ](http://godban.github.io/browsers-support-badges/)IE | [ ](http://godban.github.io/browsers-support-badges/)Firefox | [ ](http://godban.github.io/browsers-support-badges/)Chrome | [ ](http://godban.github.io/browsers-support-badges/)Safari | [ ](http://godban.github.io/browsers-support-badges/)iOS | [ ](http://godban.github.io/browsers-support-badges/)Android |
18 | |:---------:|:---------:|:---------:|:---------:|:---------:|:---------:|
19 | | IE7+ | ✓| ✓ | ✓ | ✓ | ✓ | ✓
20 |
21 | * 不支持移动端手势。
22 |
23 |
24 | ## 安装
25 |
26 | ### NPM
27 |
28 | ```bash
29 | npm install seamscroll --save
30 | ```
31 | ### CDN
32 | `https://cdn.jsdelivr.net/npm/seamscroll@0.0.11/build/seamscroll.min.js`
33 |
34 | ## 使用
35 |
36 | ```js
37 | const seamless = require('seamscroll')
38 | `or`
39 | import seamless from 'seamscroll'
40 |
41 | seamless.init({
42 | dom: document.getElementById('demo1')
43 | })
44 | //script tag
45 |
46 |
51 | ```
52 |
53 | ## Demo
54 | ```css
55 | .demo2 {
56 | width: 600px;
57 | height: 100px;
58 | position: relative;
59 | overflow: hidden;
60 | margin-top: 100px;
61 | }
62 | .list2 li {
63 | float: left;
64 | width: 100px;
65 | height: 100px;
66 | margin-right: 20px;
67 | text-align: center;
68 | font-size: 20px;
69 | color: #fff;
70 | line-height:100px;
71 | background-color: #ccc;
72 | }
73 | ```
74 | ```html
75 |
76 |
77 | 1
78 | 2
79 | 3
80 | 4
81 | 5
82 | 6
83 |
84 |
85 | ```
86 | ```javascript
87 |
88 | seamscroll.init({
89 | dom: document.getElementById('demo2'),
90 | direction: 2
91 | })
92 | ```
93 |
94 | ## 配置参数
95 | *必填参数(dom)
96 |
97 | |key|description|default|val|
98 | |:---|---|---|---|
99 | |`*dom`|作用的dom|`null`|`dom`|
100 | |`step`|步长,越大越快|`1`|`Number`|
101 | |`hoverStop`|是否启用鼠标hover控制|`true`|`Boolean`|
102 | |`direction`|方向 0 往下 1 往上 2向左 3向右|`1`|`Number`|
103 | |`singleHeight`|单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1|`0`|`Number`|
104 | |`singleWidth`|单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3|`0`|`Number`|
105 | |`waitTime`|单步停止等待时间(default 1s)|`1000`|`Number`|
106 |
107 | ## 历史版本
108 | See the GitHub [历史版本](https://github.com/chenxuan0000/seamless-scroll/releases).
109 |
110 | ## License
111 | seamless-scroll is open source and released under the [MIT License](LICENSE).
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 | 无缝滚动demo
11 |
12 |
13 |
14 |
15 |
16 |
17 | 无缝滚动第一行无缝滚动第一行 2017-12-16
18 |
19 |
20 | 无缝滚动第二行无缝滚动第一行 2017-12-16
21 |
22 |
23 | 无缝滚动第三行无缝滚动第一行 2017-12-16
24 |
25 |
26 | 无缝滚动第四行无缝滚动第一行 2017-12-16
27 |
28 |
29 | 无缝滚动第五行无缝滚动第一行 2017-12-16
30 |
31 |
32 | 无缝滚动第六行无缝滚动第一行 2017-12-16
33 |
34 |
35 | 无缝滚动第七行无缝滚动第一行 2017-12-16
36 |
37 |
38 | 无缝滚动第八行无缝滚动第一行 2017-12-16
39 |
40 |
41 |
42 |
43 |
44 | 1
45 | 2
46 | 3
47 | 4
48 | 5
49 | 6
50 |
51 |
52 |
53 |
54 | 1
55 | 2
56 | 3
57 | 4
58 | 5
59 | 6
60 |
61 |
62 |
63 |
64 |
65 | 无缝滚动第一行无缝滚动第一行 2017-12-16
66 |
67 |
68 | 无缝滚动第二行无缝滚动第一行 2017-12-16
69 |
70 |
71 | 无缝滚动第三行无缝滚动第一行 2017-12-16
72 |
73 |
74 | 无缝滚动第四行无缝滚动第一行 2017-12-16
75 |
76 |
77 | 无缝滚动第五行无缝滚动第一行 2017-12-16
78 |
79 |
80 | 无缝滚动第六行无缝滚动第一行 2017-12-16
81 |
82 |
83 | 无缝滚动第七行无缝滚动第一行 2017-12-16
84 |
85 |
86 | 无缝滚动第八行无缝滚动第一行 2017-12-16
87 |
88 |
89 |
90 |
91 |
92 |
111 |
112 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "seamscroll",
3 | "version": "0.0.12",
4 | "description": "js seamless scroll",
5 | "main": "build/seamscroll.min.js",
6 | "scripts": {
7 | "build": "node script/build.js",
8 | "build:debug": "node script/build-debug.js",
9 | "build:all": "npm run build &&npm run build:debug"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/chenxuan0000/seamless-scroll"
14 | },
15 | "keywords": [
16 | "without relying on",
17 | "js",
18 | "seamless-scroll"
19 | ],
20 | "author": "chenxuan",
21 | "license": "MIT",
22 | "dependencies": {
23 | "comutils": "^1.1.10"
24 | },
25 | "devDependencies": {
26 | "babel-core": "^6.26.0",
27 | "babel-loader": "^7.1.2",
28 | "babel-preset-env": "^1.6.1",
29 | "chalk": "^2.3.0",
30 | "mocha": "^4.0.1",
31 | "rimraf": "^2.6.2",
32 | "ora": "^1.3.0",
33 | "webpack": "^3.8.1"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/script/build-debug.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ora = require('ora')
3 | const rm = require('rimraf')
4 | const chalk = require('chalk')
5 | const webpack = require('webpack')
6 | const pkg = require('../package.json')
7 | const rootPath = path.resolve(__dirname, '../')
8 |
9 |
10 | const config = {
11 | entry: path.resolve(rootPath, 'src', 'index.js'),
12 | output: {
13 | filename: `${pkg.name}.js`,
14 | path: path.resolve(rootPath, 'build'),
15 | library: `${pkg.name}`,
16 | libraryTarget: 'umd'
17 | },
18 | module: {
19 | rules: [{
20 | test: /\.js$/,
21 | loader: 'babel-loader'
22 | }]
23 | }
24 | }
25 |
26 | new Promise(() => {
27 | // 构建全量压缩包
28 | let building = ora('building...')
29 | building.start()
30 | rm(path.resolve(rootPath, 'build', `${pkg.name}.js`), err => {
31 | if (err) throw (err)
32 | webpack(config, function (err, stats) {
33 | if (err) throw (err)
34 | building.stop()
35 | process.stdout.write(stats.toString({
36 | colors: true,
37 | modules: false,
38 | children: false,
39 | chunks: false,
40 | chunkModules: false
41 | }) + '\n\n')
42 | console.log(chalk.cyan(' Build complete.\n'))
43 | })
44 | })
45 | })
--------------------------------------------------------------------------------
/script/build.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const ora = require('ora')
3 | const rm = require('rimraf')
4 | const chalk = require('chalk')
5 | const webpack = require('webpack')
6 | const pkg = require('../package.json')
7 | const rootPath = path.resolve(__dirname, '../')
8 |
9 |
10 | const config = {
11 | entry: path.resolve(rootPath, 'src', 'index.js'),
12 | output: {
13 | filename: `${pkg.name}.min.js`,
14 | path: path.resolve(rootPath, 'build'),
15 | library: `${pkg.name}`,
16 | libraryTarget: 'umd'
17 | },
18 | module: {
19 | rules: [{
20 | test: /\.js$/,
21 | loader: 'babel-loader'
22 | }]
23 | },
24 | plugins: [
25 | new webpack.optimize.UglifyJsPlugin({
26 | compress: {screw_ie8: false},
27 | mangle: {except: ['$']},
28 | support_ie8: true,
29 | warnings: false
30 | })
31 | ]
32 | }
33 |
34 | new Promise(() => {
35 | // 构建全量压缩包
36 | let building = ora('building...')
37 | building.start()
38 | rm(path.resolve(rootPath, 'build', `${pkg.name}.min.js`), err => {
39 | if (err) throw (err)
40 | webpack(config, function (err, stats) {
41 | if (err) throw (err)
42 | building.stop()
43 | process.stdout.write(stats.toString({
44 | colors: true,
45 | modules: false,
46 | children: false,
47 | chunks: false,
48 | chunkModules: false
49 | }) + '\n\n')
50 | console.log(chalk.cyan(' Build complete.\n'))
51 | })
52 | })
53 | })
--------------------------------------------------------------------------------
/src/content/create.js:
--------------------------------------------------------------------------------
1 | import seamless from './seamless'
2 |
3 | const create = options => {
4 | return new seamless(options)
5 | }
6 |
7 | export default create
8 |
--------------------------------------------------------------------------------
/src/content/event.js:
--------------------------------------------------------------------------------
1 | const addEventListener = (element, type, callback) => {
2 | if (element.addEventListener) {
3 | element.addEventListener(type, callback, false)
4 | } else if (element.attachEvent) {
5 | element.attachEvent('on' + type, callback)
6 | } else {
7 | element['on' + type] = callback
8 | }
9 | }
10 |
11 | export default addEventListener
12 |
--------------------------------------------------------------------------------
/src/content/getCss.js:
--------------------------------------------------------------------------------
1 | const getStyle = (dom, name) => {
2 | let elem = dom.currentStyle
3 | ? dom.currentStyle
4 | : window.getComputedStyle(dom, null)
5 | return elem[name]
6 | }
7 |
8 | export default getStyle
9 |
--------------------------------------------------------------------------------
/src/content/seamless.js:
--------------------------------------------------------------------------------
1 | require('comutils/animationFrame')()
2 | const copyObj = require('comutils/copyObj')
3 | import addEventListener from './event'
4 | import getStyle from './getCss'
5 |
6 | const defaultOptions = {
7 | step: 1, //步长
8 | hoverStop: true, //是否启用鼠标hover控制
9 | direction: 1, // 0 往下 1 往上 2向左 3向右
10 | singleHeight: 0, //单条数据高度有值hoverStop关闭
11 | singleWidth: 0, //单条数据宽度有值hoverStop关闭
12 | waitTime: 1000 //单步停止等待时间
13 | }
14 |
15 | const seamless = function(options) {
16 | this.options = copyObj({}, defaultOptions, options)
17 | let dom = this.options.dom
18 | if (!dom) throw new Error('you must set a dom')
19 | dom.style.position = 'relative'
20 | dom.style.overflow = 'hidden'
21 | this.reqFrame = null
22 | this.singleWaitTime = null // single 单步滚动的定时器
23 | this._top = 0
24 | this._left = 0
25 | this.isHover = false //_move()方法的开关
26 | dom.innerHTML += dom.innerHTML
27 | if (this.options.direction > 1) {
28 | //水平向滚动
29 | let child = dom.children,
30 | childFirst = child[0],
31 | len = child.length
32 | this._width =
33 | (childFirst.offsetWidth +
34 | this._getInt(childFirst, 'margin-left') +
35 | this._getInt(childFirst, 'margin-right')) *
36 | len
37 | dom.style.width = this._width + 'px'
38 | }
39 | this._move()
40 | this._bindEvent()
41 | }
42 |
43 | seamless.prototype = {
44 | _getInt(dom, name) {
45 | return parseInt(getStyle(dom, name))
46 | },
47 | _cancle() {
48 | cancelAnimationFrame(this.reqFrame || '')
49 | },
50 | _bindEvent() {
51 | if (!this.options.hoverStop) return
52 | let that = this
53 | let dom = this.options.dom
54 | addEventListener(dom, 'mouseenter', function() {
55 | that.isHover = true // 关闭_move
56 | // 防止蛋疼的人频频hover进出单步滚动 导致定时器乱掉
57 | if (that.singleWaitTime) clearTimeout(that.singleWaitTime)
58 | that._cancle()
59 | })
60 | addEventListener(dom, 'mouseleave', function() {
61 | that.isHover = false // 开启_move
62 | that._move()
63 | })
64 | },
65 | _move() {
66 | if (this.isHover) return
67 | this._cancle()
68 | let that = this
69 | let dom = this.options.dom
70 | this.reqFrame = requestAnimationFrame(function() {
71 | let h = dom.offsetHeight / 2 //实际高度
72 | let direction = that.options.direction //滚动方向
73 | if (direction === 1) {
74 | // 上
75 | if (Math.abs(that._top) >= h) that._top = 0
76 | that._top -= that.options.step
77 | dom.style.top = that._top + 'px'
78 | } else if (direction === 0) {
79 | // 下
80 | if (that._top >= 0) that._top = h * -1
81 | that._top += that.options.step
82 | dom.style.top = that._top + 'px'
83 | } else if (direction === 2) {
84 | // 左
85 | if (Math.abs(that._left) >= that._width / 2) that._left = 0
86 | that._left -= that.options.step
87 | dom.style.left = that._left + 'px'
88 | } else if (direction === 3) {
89 | // 右
90 | if (that._left >= 0) that._left = that._width / 2 * -1
91 | that._left += that.options.step
92 | dom.style.left = that._left + 'px'
93 | }
94 | that._judgeSingle()
95 | })
96 | },
97 | _judgeSingle() {
98 | let singleH = this.options.singleHeight
99 | let singleW = this.options.singleWidth
100 | if (this.singleWaitTime) clearTimeout(this.singleWaitTime)
101 | if (!!singleH) {
102 | //是否启动了单行暂停配置
103 | if (Math.abs(this._top) % singleH === 0) {
104 | // 符合条件暂停waitTime
105 | this.singleWaitTime = setTimeout(() => {
106 | this._move()
107 | }, this.options.waitTime)
108 | } else {
109 | this._move()
110 | }
111 | } else if (!!singleW) {
112 | if (Math.abs(this._left) % singleW === 0) {
113 | // 符合条件暂停waitTime
114 | this.singleWaitTime = setTimeout(() => {
115 | this._move()
116 | }, this.options.waitTime)
117 | } else {
118 | this._move()
119 | }
120 | } else {
121 | this._move()
122 | }
123 | }
124 | }
125 |
126 | export default seamless
127 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @desc webpack打包入口
3 | */
4 |
5 | import init from './content/create'
6 |
7 | export { init }
8 |
--------------------------------------------------------------------------------
/test/demo.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Microsoft YaHei";
3 | font-size: 16px;
4 | }
5 | .clearfix {
6 | zoom: 1;
7 | }
8 |
9 | .clearfix:after {
10 | display: block;
11 | height: 0;
12 | clear: both;
13 | content: '.';
14 | visibillity: hidden;
15 | }
16 | .list li {
17 | line-height: 30px;
18 | margin-top: 10px;
19 | }
20 | .list .title {
21 | float: left;
22 | }
23 | .list .date {
24 | float: right;
25 | }
26 | /*demo1*/
27 | .demo1 {
28 | position: relative; /*兼容ie7*/
29 | width: 400px;
30 | height: 240px;
31 | overflow: hidden;
32 | }
33 |
34 | /*demo2*/
35 | .demo2 {
36 | width: 600px;
37 | height: 100px;
38 | position: relative;
39 | overflow: hidden;
40 | margin-top: 100px;
41 | }
42 | .list2 li {
43 | float: left;
44 | width: 100px;
45 | height: 100px;
46 | margin-right: 20px;
47 | text-align: center;
48 | font-size: 20px;
49 | color: #fff;
50 | line-height:100px;
51 | background-color: #ccc;
52 | }
--------------------------------------------------------------------------------
/test/reset.css:
--------------------------------------------------------------------------------
1 | /*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */
2 | html, body, p, ol, ul, li, dl, dt, dd, blockquote, figure, fieldset, legend, textarea, pre, iframe, hr, h1, h2, h3, h4, h5, h6 {
3 | margin: 0;
4 | padding: 0
5 | }
6 |
7 | h1, h2, h3, h4, h5, h6 {
8 | font-size: 100%;
9 | font-weight: normal
10 | }
11 |
12 | ul {
13 | list-style: none
14 | }
15 |
16 | button, input, select, textarea {
17 | margin: 0
18 | }
19 |
20 | html {
21 | box-sizing: border-box
22 | }
23 |
24 | *, *:before, *:after {
25 | box-sizing: inherit
26 | }
27 |
28 | img, embed, iframe, object, audio, video {
29 | height: auto;
30 | max-width: 100%
31 | }
32 |
33 | iframe {
34 | border: 0
35 | }
36 |
37 | table {
38 | border-collapse: collapse;
39 | border-spacing: 0
40 | }
41 |
42 | td, th {
43 | padding: 0;
44 | text-align: left
45 | }
--------------------------------------------------------------------------------
/test/timg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chenxuan0000/seamless-scroll/1d89d55e4d671a8c591eb4f104d7d803a457cf98/test/timg.jpg
--------------------------------------------------------------------------------