vuescroll
Version:
A powerful, customizable, multi-mode scrollbar plugin based on Vue.js
422 lines (381 loc) • 10.4 kB
JavaScript
import { isIE, isIos, touchManager, isServer } from './env';
export { isIE, isIos, touchManager, isServer };
import ZoomManager from './zoomManager';
export function deepCopy(from, to, shallow) {
if (shallow && isUndef(to)) {
return from;
}
if (isArray(from)) {
to = [];
from.forEach((item, index) => {
to[index] = deepCopy(item, to[index]);
});
} else if (from) {
if (!isPlainObj(from)) {
return from;
}
to = {};
for (let key in from) {
to[key] =
typeof from[key] === 'object'
? deepCopy(from[key], to[key])
: from[key];
}
}
return to;
}
export function mergeObject(from, to, force, shallow) {
if (shallow && isUndef(to)) {
return from;
}
to = to || {};
if (isArray(from)) {
if (!isArray(to) && force) {
to = [];
}
if (isArray(to)) {
from.forEach((item, index) => {
to[index] = mergeObject(item, to[index], force, shallow);
});
}
} else if (from) {
if (!isPlainObj(from)) {
if (force) {
to = from;
}
} else {
for (var key in from) {
if (typeof from[key] === 'object') {
if (isUndef(to[key])) {
to[key] = deepCopy(from[key], to[key], shallow);
} else {
mergeObject(from[key], to[key], force, shallow);
}
} else {
if (isUndef(to[key]) || force) to[key] = from[key];
}
}
}
}
return to;
}
export function defineReactive(target, key, source, souceKey) {
/* istanbul ignore if */
if (!source[key] && typeof source !== 'function') {
return;
}
souceKey = souceKey || key;
Object.defineProperty(target, key, {
get() {
return source[souceKey];
},
configurable: true
});
}
let scrollBarWidth;
let zoomManager;
export function getGutter() {
/* istanbul ignore next */
if (isServer()) return 0;
if (!zoomManager) {
zoomManager = new ZoomManager();
}
if (scrollBarWidth !== undefined)
return scrollBarWidth * zoomManager.getRatioBetweenPreAndCurrent();
const outer = document.createElement('div');
outer.style.visibility = 'hidden';
outer.style.width = '100px';
outer.style.position = 'absolute';
outer.style.top = '-9999px';
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = 'scroll';
const inner = document.createElement('div');
inner.style.width = '100%';
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
// multi the browser zoom
if (!zoomManager) {
zoomManager = new ZoomManager();
}
return scrollBarWidth * zoomManager.getRatioBetweenPreAndCurrent();
}
export function eventCenter(
dom,
eventName,
hander,
capture = false,
type = 'on'
) {
type == 'on'
? dom.addEventListener(eventName, hander, capture)
: dom.removeEventListener(eventName, hander, capture);
}
export const error = (msg) => {
console.error(`[vuescroll] ${msg}`);
};
export const warn = (msg) => {
console.warn(`[vuescroll] ${msg}`);
};
export function isChildInParent(child, parent) {
let flag = false;
if (!child || !parent) {
return flag;
}
while (
child.parentNode !== parent &&
child.parentNode.nodeType !== 9 &&
!child.parentNode._isVuescroll
) {
child = child.parentNode;
}
if (child.parentNode == parent) {
flag = true;
}
return flag;
}
export function getPrefix(global) {
var docStyle = document.documentElement.style;
var engine;
/* istanbul ignore if */
if (
global.opera &&
Object.prototype.toString.call(opera) === '[object Opera]'
) {
engine = 'presto';
} /* istanbul ignore next */ else if ('MozAppearance' in docStyle) {
engine = 'gecko';
} else if ('WebkitAppearance' in docStyle) {
engine = 'webkit';
} /* istanbul ignore next */ else if (
typeof navigator.cpuClass === 'string'
) {
engine = 'trident';
}
var vendorPrefix = {
trident: 'ms',
gecko: 'moz',
webkit: 'webkit',
presto: 'O'
}[engine];
return vendorPrefix;
}
export function getComplitableStyle(property, value) {
/* istanbul ignore if */
if (isServer()) return false;
const compatibleValue = `-${getPrefix(window)}-${value}`;
const testElm = document.createElement('div');
testElm.style[property] = compatibleValue;
if (testElm.style[property] == compatibleValue) {
return compatibleValue;
}
/* istanbul ignore next */
return false;
}
/**
* Insert children into user-passed slot at vnode level
*/
export function insertChildrenIntoSlot(
h,
parentVnode = [],
childVNode = [],
data = {},
swapChildren
) {
/* istanbul ignore if */
if (parentVnode && parentVnode.length > 1) {
return swapChildren
? [...childVNode, ...parentVnode]
: [...parentVnode, ...childVNode];
}
parentVnode = parentVnode[0];
let { ch, tag, isComponent } = getVnodeInfo(parentVnode);
if (isComponent) {
parentVnode.data = mergeObject(
{ attrs: parentVnode.componentOptions.propsData },
parentVnode.data,
false, // force: false
true // shallow: true
);
}
ch = swapChildren ? [...childVNode, ...ch] : [...ch, ...childVNode];
delete parentVnode.data.slot;
return h(tag, mergeObject(data, parentVnode.data, false, true), ch);
}
/**
* Get the info of a vnode,
* vnode must be parentVnode
*/
export function getVnodeInfo(vnode) {
if (!vnode || vnode.length > 1) return {};
vnode = vnode[0] ? vnode[0] : vnode;
const isComponent = !!vnode.componentOptions;
let ch;
let tag;
if (isComponent) {
ch = vnode.componentOptions.children || [];
tag = vnode.componentOptions.tag;
} else {
ch = vnode.children || [];
tag = vnode.tag;
}
return {
isComponent,
ch,
tag
};
}
/**
* Get the vuescroll instance instead of
* user pass component like slot.
*/
export function getRealParent(ctx) {
let parent = ctx.$parent;
if (!parent._isVuescrollRoot && parent) {
parent = parent.$parent;
}
return parent;
}
export const isArray = (_) => Array.isArray(_);
export const isPlainObj = (_) =>
Object.prototype.toString.call(_) == '[object Object]';
export const isUndef = (_) => typeof _ === 'undefined';
export function getNumericValue(distance, size) {
let number;
if (!(number = /(-?\d+(?:\.\d+?)?)%$/.exec(distance))) {
number = distance - 0;
} else {
number = number[1] - 0;
number = (size * number) / 100;
}
return number;
}
export function createStyle(styleId, cssText) {
/* istanbul ignore if */
if (isServer() || document.getElementById(styleId)) {
return;
}
const head = document.head || doc.getElementsByTagName('head')[0];
const style = document.createElement('style');
style.id = styleId;
style.type = 'text/css';
/* istanbul ignore if */
if (style.styleSheet) {
style.styleSheet.cssText = cssText;
} else {
style.appendChild(document.createTextNode(cssText));
}
head.appendChild(style);
}
// Hide the ios native scrollbar.
export function createHideBarStyle() {
/* istanbul ignore next */
{
const cssText = `.__hidebar::-webkit-scrollbar {
width: 0;
height: 0;
}`;
createStyle('vuescroll-hide-ios-bar', cssText);
}
}
// create slide mode style
export function createSlideModeStyle() {
const cssText = `
@-webkit-keyframes loading-rotate {
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
@keyframes loading-rotate {
to {
-webkit-transform: rotate(1turn);
transform: rotate(1turn);
}
}
@-webkit-keyframes loading-wipe {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
to {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
@keyframes loading-wipe {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -40px;
}
to {
stroke-dasharray: 90, 150;
stroke-dashoffset: -120px;
}
}
.__vuescroll .__refresh,
.__vuescroll .__load {
position: absolute;
width: 100%;
color: black;
height: 50px;
line-height: 50px;
text-align: center;
font-size: 16px;
}
.__vuescroll .__refresh svg,
.__vuescroll .__load svg {
margin-right: 10px;
width: 25px;
height: 25px;
vertical-align: sub;
}
.__vuescroll .__refresh svg.active,
.__vuescroll .__load svg.active {
transition: all 0.2s;
}
.__vuescroll .__refresh svg.active.deactive,
.__vuescroll .__load svg.active.deactive {
transform: rotateZ(180deg);
}
.__vuescroll .__refresh svg path,
.__vuescroll .__refresh svg rect,
.__vuescroll .__load svg path,
.__vuescroll .__load svg rect {
fill: #20a0ff;
}
.__vuescroll .__refresh svg.start,
.__vuescroll .__load svg.start {
stroke: #343640;
stroke-width: 4;
stroke-linecap: round;
-webkit-animation: loading-rotate 2s linear infinite;
animation: loading-rotate 2s linear infinite;
}
.__vuescroll .__refresh svg.start .bg-path,
.__vuescroll .__load svg.start .bg-path {
stroke: #f2f2f2;
fill: none;
}
.__vuescroll .__refresh svg.start .active-path,
.__vuescroll .__load svg.start .active-path {
stroke: #20a0ff;
fill: none;
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
-webkit-animation: loading-wipe 1.5s ease-in-out infinite;
animation: loading-wipe 1.5s ease-in-out infinite;
}
`;
createStyle('vuescroll-silde-mode-style', cssText);
}