318 lines
13 KiB
JavaScript
Executable File
318 lines
13 KiB
JavaScript
Executable File
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
var __extends = (this && this.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
import { Emitter } from './event.js';
|
|
import { Disposable } from './lifecycle.js';
|
|
var ScrollState = /** @class */ (function () {
|
|
function ScrollState(width, scrollWidth, scrollLeft, height, scrollHeight, scrollTop) {
|
|
width = width | 0;
|
|
scrollWidth = scrollWidth | 0;
|
|
scrollLeft = scrollLeft | 0;
|
|
height = height | 0;
|
|
scrollHeight = scrollHeight | 0;
|
|
scrollTop = scrollTop | 0;
|
|
if (width < 0) {
|
|
width = 0;
|
|
}
|
|
if (scrollLeft + width > scrollWidth) {
|
|
scrollLeft = scrollWidth - width;
|
|
}
|
|
if (scrollLeft < 0) {
|
|
scrollLeft = 0;
|
|
}
|
|
if (height < 0) {
|
|
height = 0;
|
|
}
|
|
if (scrollTop + height > scrollHeight) {
|
|
scrollTop = scrollHeight - height;
|
|
}
|
|
if (scrollTop < 0) {
|
|
scrollTop = 0;
|
|
}
|
|
this.width = width;
|
|
this.scrollWidth = scrollWidth;
|
|
this.scrollLeft = scrollLeft;
|
|
this.height = height;
|
|
this.scrollHeight = scrollHeight;
|
|
this.scrollTop = scrollTop;
|
|
}
|
|
ScrollState.prototype.equals = function (other) {
|
|
return (this.width === other.width
|
|
&& this.scrollWidth === other.scrollWidth
|
|
&& this.scrollLeft === other.scrollLeft
|
|
&& this.height === other.height
|
|
&& this.scrollHeight === other.scrollHeight
|
|
&& this.scrollTop === other.scrollTop);
|
|
};
|
|
ScrollState.prototype.withScrollDimensions = function (update) {
|
|
return new ScrollState((typeof update.width !== 'undefined' ? update.width : this.width), (typeof update.scrollWidth !== 'undefined' ? update.scrollWidth : this.scrollWidth), this.scrollLeft, (typeof update.height !== 'undefined' ? update.height : this.height), (typeof update.scrollHeight !== 'undefined' ? update.scrollHeight : this.scrollHeight), this.scrollTop);
|
|
};
|
|
ScrollState.prototype.withScrollPosition = function (update) {
|
|
return new ScrollState(this.width, this.scrollWidth, (typeof update.scrollLeft !== 'undefined' ? update.scrollLeft : this.scrollLeft), this.height, this.scrollHeight, (typeof update.scrollTop !== 'undefined' ? update.scrollTop : this.scrollTop));
|
|
};
|
|
ScrollState.prototype.createScrollEvent = function (previous) {
|
|
var widthChanged = (this.width !== previous.width);
|
|
var scrollWidthChanged = (this.scrollWidth !== previous.scrollWidth);
|
|
var scrollLeftChanged = (this.scrollLeft !== previous.scrollLeft);
|
|
var heightChanged = (this.height !== previous.height);
|
|
var scrollHeightChanged = (this.scrollHeight !== previous.scrollHeight);
|
|
var scrollTopChanged = (this.scrollTop !== previous.scrollTop);
|
|
return {
|
|
width: this.width,
|
|
scrollWidth: this.scrollWidth,
|
|
scrollLeft: this.scrollLeft,
|
|
height: this.height,
|
|
scrollHeight: this.scrollHeight,
|
|
scrollTop: this.scrollTop,
|
|
widthChanged: widthChanged,
|
|
scrollWidthChanged: scrollWidthChanged,
|
|
scrollLeftChanged: scrollLeftChanged,
|
|
heightChanged: heightChanged,
|
|
scrollHeightChanged: scrollHeightChanged,
|
|
scrollTopChanged: scrollTopChanged,
|
|
};
|
|
};
|
|
return ScrollState;
|
|
}());
|
|
export { ScrollState };
|
|
var Scrollable = /** @class */ (function (_super) {
|
|
__extends(Scrollable, _super);
|
|
function Scrollable(smoothScrollDuration, scheduleAtNextAnimationFrame) {
|
|
var _this = _super.call(this) || this;
|
|
_this._onScroll = _this._register(new Emitter());
|
|
_this.onScroll = _this._onScroll.event;
|
|
_this._smoothScrollDuration = smoothScrollDuration;
|
|
_this._scheduleAtNextAnimationFrame = scheduleAtNextAnimationFrame;
|
|
_this._state = new ScrollState(0, 0, 0, 0, 0, 0);
|
|
_this._smoothScrolling = null;
|
|
return _this;
|
|
}
|
|
Scrollable.prototype.dispose = function () {
|
|
if (this._smoothScrolling) {
|
|
this._smoothScrolling.dispose();
|
|
this._smoothScrolling = null;
|
|
}
|
|
_super.prototype.dispose.call(this);
|
|
};
|
|
Scrollable.prototype.setSmoothScrollDuration = function (smoothScrollDuration) {
|
|
this._smoothScrollDuration = smoothScrollDuration;
|
|
};
|
|
Scrollable.prototype.validateScrollPosition = function (scrollPosition) {
|
|
return this._state.withScrollPosition(scrollPosition);
|
|
};
|
|
Scrollable.prototype.getScrollDimensions = function () {
|
|
return this._state;
|
|
};
|
|
Scrollable.prototype.setScrollDimensions = function (dimensions) {
|
|
var newState = this._state.withScrollDimensions(dimensions);
|
|
this._setState(newState);
|
|
// Validate outstanding animated scroll position target
|
|
if (this._smoothScrolling) {
|
|
this._smoothScrolling.acceptScrollDimensions(this._state);
|
|
}
|
|
};
|
|
/**
|
|
* Returns the final scroll position that the instance will have once the smooth scroll animation concludes.
|
|
* If no scroll animation is occurring, it will return the current scroll position instead.
|
|
*/
|
|
Scrollable.prototype.getFutureScrollPosition = function () {
|
|
if (this._smoothScrolling) {
|
|
return this._smoothScrolling.to;
|
|
}
|
|
return this._state;
|
|
};
|
|
/**
|
|
* Returns the current scroll position.
|
|
* Note: This result might be an intermediate scroll position, as there might be an ongoing smooth scroll animation.
|
|
*/
|
|
Scrollable.prototype.getCurrentScrollPosition = function () {
|
|
return this._state;
|
|
};
|
|
Scrollable.prototype.setScrollPositionNow = function (update) {
|
|
// no smooth scrolling requested
|
|
var newState = this._state.withScrollPosition(update);
|
|
// Terminate any outstanding smooth scrolling
|
|
if (this._smoothScrolling) {
|
|
this._smoothScrolling.dispose();
|
|
this._smoothScrolling = null;
|
|
}
|
|
this._setState(newState);
|
|
};
|
|
Scrollable.prototype.setScrollPositionSmooth = function (update) {
|
|
var _this = this;
|
|
if (this._smoothScrollDuration === 0) {
|
|
// Smooth scrolling not supported.
|
|
return this.setScrollPositionNow(update);
|
|
}
|
|
if (this._smoothScrolling) {
|
|
// Combine our pending scrollLeft/scrollTop with incoming scrollLeft/scrollTop
|
|
update = {
|
|
scrollLeft: (typeof update.scrollLeft === 'undefined' ? this._smoothScrolling.to.scrollLeft : update.scrollLeft),
|
|
scrollTop: (typeof update.scrollTop === 'undefined' ? this._smoothScrolling.to.scrollTop : update.scrollTop)
|
|
};
|
|
// Validate `update`
|
|
var validTarget = this._state.withScrollPosition(update);
|
|
if (this._smoothScrolling.to.scrollLeft === validTarget.scrollLeft && this._smoothScrolling.to.scrollTop === validTarget.scrollTop) {
|
|
// No need to interrupt or extend the current animation since we're going to the same place
|
|
return;
|
|
}
|
|
var newSmoothScrolling = this._smoothScrolling.combine(this._state, validTarget, this._smoothScrollDuration);
|
|
this._smoothScrolling.dispose();
|
|
this._smoothScrolling = newSmoothScrolling;
|
|
}
|
|
else {
|
|
// Validate `update`
|
|
var validTarget = this._state.withScrollPosition(update);
|
|
this._smoothScrolling = SmoothScrollingOperation.start(this._state, validTarget, this._smoothScrollDuration);
|
|
}
|
|
// Begin smooth scrolling animation
|
|
this._smoothScrolling.animationFrameDisposable = this._scheduleAtNextAnimationFrame(function () {
|
|
if (!_this._smoothScrolling) {
|
|
return;
|
|
}
|
|
_this._smoothScrolling.animationFrameDisposable = null;
|
|
_this._performSmoothScrolling();
|
|
});
|
|
};
|
|
Scrollable.prototype._performSmoothScrolling = function () {
|
|
var _this = this;
|
|
if (!this._smoothScrolling) {
|
|
return;
|
|
}
|
|
var update = this._smoothScrolling.tick();
|
|
var newState = this._state.withScrollPosition(update);
|
|
this._setState(newState);
|
|
if (update.isDone) {
|
|
this._smoothScrolling.dispose();
|
|
this._smoothScrolling = null;
|
|
return;
|
|
}
|
|
// Continue smooth scrolling animation
|
|
this._smoothScrolling.animationFrameDisposable = this._scheduleAtNextAnimationFrame(function () {
|
|
if (!_this._smoothScrolling) {
|
|
return;
|
|
}
|
|
_this._smoothScrolling.animationFrameDisposable = null;
|
|
_this._performSmoothScrolling();
|
|
});
|
|
};
|
|
Scrollable.prototype._setState = function (newState) {
|
|
var oldState = this._state;
|
|
if (oldState.equals(newState)) {
|
|
// no change
|
|
return;
|
|
}
|
|
this._state = newState;
|
|
this._onScroll.fire(this._state.createScrollEvent(oldState));
|
|
};
|
|
return Scrollable;
|
|
}(Disposable));
|
|
export { Scrollable };
|
|
var SmoothScrollingUpdate = /** @class */ (function () {
|
|
function SmoothScrollingUpdate(scrollLeft, scrollTop, isDone) {
|
|
this.scrollLeft = scrollLeft;
|
|
this.scrollTop = scrollTop;
|
|
this.isDone = isDone;
|
|
}
|
|
return SmoothScrollingUpdate;
|
|
}());
|
|
export { SmoothScrollingUpdate };
|
|
function createEaseOutCubic(from, to) {
|
|
var delta = to - from;
|
|
return function (completion) {
|
|
return from + delta * easeOutCubic(completion);
|
|
};
|
|
}
|
|
function createComposed(a, b, cut) {
|
|
return function (completion) {
|
|
if (completion < cut) {
|
|
return a(completion / cut);
|
|
}
|
|
return b((completion - cut) / (1 - cut));
|
|
};
|
|
}
|
|
var SmoothScrollingOperation = /** @class */ (function () {
|
|
function SmoothScrollingOperation(from, to, startTime, duration) {
|
|
this.from = from;
|
|
this.to = to;
|
|
this.duration = duration;
|
|
this._startTime = startTime;
|
|
this.animationFrameDisposable = null;
|
|
this._initAnimations();
|
|
}
|
|
SmoothScrollingOperation.prototype._initAnimations = function () {
|
|
this.scrollLeft = this._initAnimation(this.from.scrollLeft, this.to.scrollLeft, this.to.width);
|
|
this.scrollTop = this._initAnimation(this.from.scrollTop, this.to.scrollTop, this.to.height);
|
|
};
|
|
SmoothScrollingOperation.prototype._initAnimation = function (from, to, viewportSize) {
|
|
var delta = Math.abs(from - to);
|
|
if (delta > 2.5 * viewportSize) {
|
|
var stop1 = void 0, stop2 = void 0;
|
|
if (from < to) {
|
|
// scroll to 75% of the viewportSize
|
|
stop1 = from + 0.75 * viewportSize;
|
|
stop2 = to - 0.75 * viewportSize;
|
|
}
|
|
else {
|
|
stop1 = from - 0.75 * viewportSize;
|
|
stop2 = to + 0.75 * viewportSize;
|
|
}
|
|
return createComposed(createEaseOutCubic(from, stop1), createEaseOutCubic(stop2, to), 0.33);
|
|
}
|
|
return createEaseOutCubic(from, to);
|
|
};
|
|
SmoothScrollingOperation.prototype.dispose = function () {
|
|
if (this.animationFrameDisposable !== null) {
|
|
this.animationFrameDisposable.dispose();
|
|
this.animationFrameDisposable = null;
|
|
}
|
|
};
|
|
SmoothScrollingOperation.prototype.acceptScrollDimensions = function (state) {
|
|
this.to = state.withScrollPosition(this.to);
|
|
this._initAnimations();
|
|
};
|
|
SmoothScrollingOperation.prototype.tick = function () {
|
|
return this._tick(Date.now());
|
|
};
|
|
SmoothScrollingOperation.prototype._tick = function (now) {
|
|
var completion = (now - this._startTime) / this.duration;
|
|
if (completion < 1) {
|
|
var newScrollLeft = this.scrollLeft(completion);
|
|
var newScrollTop = this.scrollTop(completion);
|
|
return new SmoothScrollingUpdate(newScrollLeft, newScrollTop, false);
|
|
}
|
|
return new SmoothScrollingUpdate(this.to.scrollLeft, this.to.scrollTop, true);
|
|
};
|
|
SmoothScrollingOperation.prototype.combine = function (from, to, duration) {
|
|
return SmoothScrollingOperation.start(from, to, duration);
|
|
};
|
|
SmoothScrollingOperation.start = function (from, to, duration) {
|
|
// +10 / -10 : pretend the animation already started for a quicker response to a scroll request
|
|
duration = duration + 10;
|
|
var startTime = Date.now() - 10;
|
|
return new SmoothScrollingOperation(from, to, startTime, duration);
|
|
};
|
|
return SmoothScrollingOperation;
|
|
}());
|
|
export { SmoothScrollingOperation };
|
|
function easeInCubic(t) {
|
|
return Math.pow(t, 3);
|
|
}
|
|
function easeOutCubic(t) {
|
|
return 1 - easeInCubic(1 - t);
|
|
}
|