使い方のmemo:
//optionは省略可
let option = {
interval : setInterval のタイマー値,
offset : new Point (x座標, y座標),
accell : new Point (x座標における加速度, y座標における加速度),
position: "top middle bottom left center right" //2つ指定すると吉
}
new Elevator (ターゲットとなる要素, option);
code
{
const
abs = Math.abs,
minimum = 1e-2,
fx = (a, b, c, d) => 1 < abs (d = a - b) ? d / c: d;
class Point {
constructor (x = 0, y = 0) { this.x = x ; this.y = y; }
add ({x, y}) { this.x += x; this.y += y; return this; }
sub ({x, y}) { this.x -= x; this.y -= y; return this; }
multiply ({x, y}) { this.x *= x; this.y *= y; return this; }
division ({x, y}) { this.x /= x; this.y /= y; return this; }
copy () { return new Point (this.x, this.y); }
reduction ({x, y}, {x:sx, y:sy}) {
this.x -= fx (this.x, x, sx);
this.y -= fx (this.y, y, sy);
return this;
}
equal ({x, y}) {
return ! (minimum < abs (this.x - x) || minimum < abs (this.y - y));
}
}
this.Point = Point;
}
{
const
DEF_OPTIONS = {
interval : 10,
offset : new Point (0, 0),
accell : new Point (25, 25),
position : 'bottom right'
},
getIdealPoint =
function () {
let
e = this.target,
d = e.ownerDocument,
b = d.compatMode == 'CSS1Compat' ? d.documentElement: d.body;
return new Point (b.clientWidth, b.clientHeight)
.sub (new Point (e.offsetWidth, e.offsetHeight))
.multiply (this.matrix)
.add (this.options.offset);
},
getScroll =
function () {
let v = this.target.ownerDocument.defaultView;
return new Point (v.pageXOffset, v.pageYOffset);
},
Locator =
function (p) {
let {x, y} = p;
this.left = x + 'px';
this.top = y + 'px';
},
Decorator =
function (alpha) {
alpha = Math.min (Math.max (0, alpha), 100);
this.visibility = alpha ? 'visible': 'hidden';
this.opacity = String (alpha / 100);
},
Init =
function (target, arg_options) {
this.target = target;
this.options = Object.assign ({ }, DEF_OPTIONS, arg_options);
this.timerId = null;
let
x, y,
win = target.ownerDocument.defaultView,
style = target.style,
pstr = this.options.position.split (' ');
pstr.forEach (str => {
switch (str) {
case 'top' : y = 0; break;
case 'middle' : y = 0.5; break;
case 'bottom' : y = 1; break;
case 'left' : x = 0; break;
case 'center' : x = 0.5; break;
case 'right' : x = 1; break;
}
});
this.matrix = new Point (x, y);
style.cssText = 'margin: 0; position: absolute; white-space: nowrap;';
Locator.call (style, getIdealPoint.call (this).add (getScroll.call(this)));
win.addEventListener ('scroll', this, false);
win.addEventListener ('resize', this, false);
};
class Elevator {
constructor (target, arg_options) {
if (1 > arguments.length)
throw new Error ('対象となる要素がありません');
Init.call (this, target, arg_options);
this.start ();
}
start () {
if (! this.timerId)
this.timerId = setInterval (this.move.bind (this), this.options.interval);
}
stop () {
if (this.timerId) {
clearInterval (this.timerId);
this.timerId = null;
Decorator.call (this.target.style, 0);
}
}
handleEvent (event) {
this.start ();
}
move () {
let
target = this.target,
style = target.style,
rect = target.getBoundingClientRect (),
scroll = getScroll.call (this),
current = new Point (rect.left, rect.top),
ideal = getIdealPoint.call (this);
current.reduction (ideal, this.options.accell);
if (current.equal (ideal))
this.stop ();
Decorator.call (style, scroll.y);
Locator.call (style, current.add (scroll));
}
}
this.Elevator = Elevator;
}