diva.js 6.0.1 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/.clang-format +7 -0
  2. package/.github/workflows/npm-publish.yml +45 -0
  3. package/LICENSE +55 -0
  4. package/Makefile +75 -0
  5. package/README.md +15 -108
  6. package/elm.json +32 -0
  7. package/package.json +12 -59
  8. package/review/elm.json +52 -0
  9. package/review/src/ReviewConfig.elm +87 -0
  10. package/scripts/elm-esm.sh +40 -0
  11. package/scripts/minify-css.mjs +31 -0
  12. package/src/Filters.elm +1044 -0
  13. package/src/Main.elm +1217 -0
  14. package/src/Model.elm +213 -0
  15. package/src/Msg.elm +59 -0
  16. package/src/Utilities.elm +46 -0
  17. package/src/View/CollectionExplorer.elm +172 -0
  18. package/src/View/Helpers.elm +86 -0
  19. package/src/View/HtmlRenderer.elm +136 -0
  20. package/src/View/Icons.elm +159 -0
  21. package/src/View/ManifestInfoModal.elm +363 -0
  22. package/src/View/PageViewModal.elm +1046 -0
  23. package/src/View/Sidebar.elm +786 -0
  24. package/src/View/Toolbar.elm +189 -0
  25. package/src/View.elm +244 -0
  26. package/src/diva.ts +802 -0
  27. package/src/filters.ts +1843 -0
  28. package/src/styles/app.css +328 -0
  29. package/src/styles/collection.css +75 -0
  30. package/src/styles/modal.css +388 -0
  31. package/src/styles/sidebar.css +215 -0
  32. package/src/styles/theme.css +39 -0
  33. package/src/styles/toolbar.css +154 -0
  34. package/src/viewer-element.ts +1307 -0
  35. package/testing/index.html +52 -0
  36. package/testing/testing.html +231 -0
  37. package/tsconfig.json +12 -0
  38. package/AUTHORS +0 -22
  39. package/_site/diva.iml +0 -11
  40. package/build/diva.css +0 -554
  41. package/build/diva.css.map +0 -1
  42. package/build/diva.js +0 -9
  43. package/build/diva.js.map +0 -1
  44. package/build/plugins/download.js +0 -2
  45. package/build/plugins/download.js.map +0 -1
  46. package/build/plugins/manipulation.js +0 -2
  47. package/build/plugins/manipulation.js.map +0 -1
  48. package/build/plugins/metadata.js +0 -2
  49. package/build/plugins/metadata.js.map +0 -1
  50. package/diva.iml +0 -11
  51. package/index.html +0 -28
  52. package/karma.conf.js +0 -87
  53. package/source/css/_mixins.scss +0 -43
  54. package/source/css/_variables.scss +0 -50
  55. package/source/css/_viewer.scss +0 -462
  56. package/source/css/diva.scss +0 -15
  57. package/source/css/plugins/_manipulation.scss +0 -228
  58. package/source/css/plugins/_metadata.scss +0 -31
  59. package/source/img/adjust.svg +0 -11
  60. package/source/img/book-view.svg +0 -6
  61. package/source/img/close.svg +0 -6
  62. package/source/img/download.svg +0 -6
  63. package/source/img/from-fullscreen.svg +0 -8
  64. package/source/img/grid-fewer.svg +0 -6
  65. package/source/img/grid-more.svg +0 -6
  66. package/source/img/grid-view.svg +0 -6
  67. package/source/img/link.svg +0 -6
  68. package/source/img/metadata.svg +0 -9
  69. package/source/img/page-view.svg +0 -6
  70. package/source/img/to-fullscreen.svg +0 -11
  71. package/source/img/zoom-in.svg +0 -6
  72. package/source/img/zoom-out.svg +0 -7
  73. package/source/js/composite-image.js +0 -174
  74. package/source/js/diva-global.js +0 -7
  75. package/source/js/diva.js +0 -1543
  76. package/source/js/document-handler.js +0 -180
  77. package/source/js/document-layout.js +0 -286
  78. package/source/js/exceptions.js +0 -26
  79. package/source/js/gesture-events.js +0 -190
  80. package/source/js/grid-handler.js +0 -122
  81. package/source/js/iiif-source-adapter.js +0 -63
  82. package/source/js/image-cache.js +0 -113
  83. package/source/js/image-manifest.js +0 -157
  84. package/source/js/image-request-handler.js +0 -76
  85. package/source/js/interpolate-animation.js +0 -122
  86. package/source/js/page-layouts/book-layout.js +0 -161
  87. package/source/js/page-layouts/grid-layout.js +0 -97
  88. package/source/js/page-layouts/index.js +0 -38
  89. package/source/js/page-layouts/page-dimensions.js +0 -9
  90. package/source/js/page-layouts/singles-layout.js +0 -27
  91. package/source/js/page-overlay-manager.js +0 -102
  92. package/source/js/page-tools-overlay.js +0 -95
  93. package/source/js/parse-iiif-manifest.js +0 -302
  94. package/source/js/plugins/_filters.js +0 -679
  95. package/source/js/plugins/download.js +0 -83
  96. package/source/js/plugins/manipulation.js +0 -837
  97. package/source/js/plugins/metadata.js +0 -190
  98. package/source/js/renderer.js +0 -584
  99. package/source/js/settings-view.js +0 -30
  100. package/source/js/tile-coverage-map.js +0 -25
  101. package/source/js/toolbar.js +0 -572
  102. package/source/js/utils/dragscroll.js +0 -106
  103. package/source/js/utils/elt.js +0 -94
  104. package/source/js/utils/events.js +0 -190
  105. package/source/js/utils/get-scrollbar-width.js +0 -29
  106. package/source/js/utils/hash-params.js +0 -86
  107. package/source/js/utils/parse-label-value.js +0 -34
  108. package/source/js/utils/vanilla.kinetic.js +0 -527
  109. package/source/js/validation-runner.js +0 -177
  110. package/source/js/viewer-core.js +0 -1505
  111. package/source/js/viewport.js +0 -143
  112. package/test/_setup.js +0 -13
  113. package/test/composite-image_test.js +0 -94
  114. package/test/diva_test.js +0 -43
  115. package/test/hash-params_test.js +0 -221
  116. package/test/image-cache_test.js +0 -106
  117. package/test/main.js +0 -6
  118. package/test/manifests/beromunsterManifest.json +0 -15514
  119. package/test/manifests/iiifv2.json +0 -11032
  120. package/test/manifests/iiifv2pages.json +0 -30437
  121. package/test/manifests/iiifv3.json +0 -10965
  122. package/test/navigation_test.js +0 -355
  123. package/test/parse-iiif-manifest_test.js +0 -68
  124. package/test/public_test.js +0 -881
  125. package/test/settings_test.js +0 -487
  126. package/test/utils/book-layout_test.js +0 -148
  127. package/test/utils/elt_test.js +0 -102
  128. package/test/utils/events_test.js +0 -245
  129. package/test/utils/hash-params_test.js +0 -79
  130. package/test/utils/parse-label-value_test.js +0 -45
  131. package/test/z_plugins_test.js +0 -180
  132. package/webpack.config.js +0 -58
  133. package/webpack.config.test.js +0 -45
@@ -1,122 +0,0 @@
1
- import maxBy from 'lodash.maxby';
2
-
3
- export default class GridHandler
4
- {
5
- constructor (viewerCore)
6
- {
7
- this._viewerCore = viewerCore;
8
- }
9
-
10
- // USER EVENTS
11
- onDoubleClick (event, coords)
12
- {
13
- const position = this._viewerCore.getPagePositionAtViewportOffset(coords);
14
-
15
- const layout = this._viewerCore.getCurrentLayout();
16
- const viewport = this._viewerCore.getViewport();
17
- const pageToViewportCenterOffset = layout.getPageToViewportCenterOffset(position.anchorPage, viewport);
18
-
19
- this._viewerCore.reload({
20
- inGrid: false,
21
- goDirectlyTo: position.anchorPage,
22
- horizontalOffset: pageToViewportCenterOffset.x + position.offset.left,
23
- verticalOffset: pageToViewportCenterOffset.y + position.offset.top
24
- });
25
- }
26
-
27
- onPinch ()
28
- {
29
- this._viewerCore.reload({inGrid: false});
30
- }
31
-
32
- // VIEW EVENTS
33
- onViewWillLoad ()
34
- {
35
- // FIXME(wabain): Should something happen here?
36
- /* No-op */
37
- }
38
-
39
- onViewDidLoad ()
40
- {
41
- // FIXME(wabain): Should something happen here?
42
- /* No-op */
43
- }
44
-
45
- onViewDidUpdate (renderedPages, targetPage)
46
- {
47
- // return early if there are no rendered pages in view.
48
- if (renderedPages.length === 0) return;
49
-
50
- // calculate the visible pages from the rendered pages
51
- let temp = this._viewerCore.viewerState.viewport.intersectionTolerance;
52
- // without setting to 0, isPageVisible returns true for pages out of viewport by intersectionTolerance
53
- this._viewerCore.viewerState.viewport.intersectionTolerance = 0;
54
- let visiblePages = renderedPages.filter(index => this._viewerCore.viewerState.renderer.isPageVisible(index));
55
- // reset back to original value after getting true visible pages
56
- this._viewerCore.viewerState.viewport.intersectionTolerance = temp;
57
-
58
- if (targetPage !== null)
59
- {
60
- this._viewerCore.setCurrentPages(targetPage, visiblePages);
61
- return;
62
- }
63
-
64
- // Select the current page from the first row if it is fully visible, or from
65
- // the second row if it is fully visible, or from the centermost row otherwise.
66
- // If the current page is in that group then don't change it. Otherwise, set
67
- // the current page to the group's first page.
68
-
69
- const layout = this._viewerCore.getCurrentLayout();
70
- const groups = [];
71
-
72
- renderedPages.forEach(pageIndex =>
73
- {
74
- const group = layout.getPageInfo(pageIndex).group;
75
- if (groups.length === 0 || group !== groups[groups.length - 1])
76
- {
77
- groups.push(group);
78
- }
79
- });
80
-
81
- const viewport = this._viewerCore.getViewport();
82
- let chosenGroup;
83
-
84
- if (groups.length === 1 || groups[0].region.top >= viewport.top)
85
- {
86
- chosenGroup = groups[0];
87
- }
88
- else if (groups[1].region.bottom <= viewport.bottom)
89
- {
90
- chosenGroup = groups[1];
91
- }
92
- else
93
- {
94
- chosenGroup = getCentermostGroup(groups, viewport);
95
- }
96
-
97
- const currentPage = this._viewerCore.getSettings().activePageIndex;
98
-
99
- const hasCurrentPage = chosenGroup.pages.some(page => page.index === currentPage);
100
-
101
- if (!hasCurrentPage)
102
- {
103
- this._viewerCore.setCurrentPages(chosenGroup.pages[0].index, visiblePages);
104
- }
105
- }
106
-
107
- destroy ()
108
- {
109
- // No-op
110
- }
111
- }
112
-
113
- function getCentermostGroup (groups, viewport)
114
- {
115
- const viewportMiddle = viewport.top + viewport.height / 2;
116
-
117
- return maxBy(groups, group =>
118
- {
119
- const groupMiddle = group.region.top + group.dimensions.height / 2;
120
- return -Math.abs(viewportMiddle - groupMiddle);
121
- });
122
- }
@@ -1,63 +0,0 @@
1
- export default class IIIFSourceAdapter
2
- {
3
- getPageImageURL (manifest, pageIndex, size)
4
- {
5
- let dimens;
6
-
7
- if (!size || (size.width == null && size.height == null))
8
- {
9
- dimens = 'full';
10
- }
11
- else
12
- {
13
- dimens = (size.width == null ? '' : size.width) + ',' + (size.height == null ? '' : size.height);
14
- }
15
-
16
- const page = manifest.pages[pageIndex];
17
- const quality = (page.api > 1.1) ? 'default' : 'native';
18
-
19
- return encodeURI(page.url + 'full/' + dimens + '/0/' + quality + '.jpg');
20
- }
21
-
22
- getTileImageURL (manifest, pageIndex, params)
23
- {
24
- const page = manifest.pages[pageIndex];
25
-
26
- let height, width;
27
-
28
- if (params.row === params.rowCount - 1)
29
- {
30
- height = page.d[params.zoomLevel].h - (params.rowCount - 1) * params.tileDimensions.height;
31
- }
32
- else
33
- {
34
- height = params.tileDimensions.height;
35
- }
36
-
37
- if (params.col === params.colCount - 1)
38
- {
39
- width = page.d[params.zoomLevel].w - (params.colCount - 1) * params.tileDimensions.width;
40
- }
41
- else
42
- {
43
- width = params.tileDimensions.width;
44
- }
45
-
46
- const zoomDifference = Math.pow(2, manifest.maxZoom - params.zoomLevel);
47
-
48
- let x = params.col * params.tileDimensions.width * zoomDifference;
49
- let y = params.row * params.tileDimensions.height * zoomDifference;
50
-
51
- if (page.hasOwnProperty('xoffset'))
52
- {
53
- x += page.xoffset;
54
- y += page.yoffset;
55
- }
56
-
57
- const region = [x, y, width * zoomDifference, height * zoomDifference].join(',');
58
-
59
- const quality = (page.api > 1.1) ? 'default' : 'native';
60
-
61
- return encodeURI(page.url + region + '/' + width + ',' + height + '/0/' + quality + '.jpg');
62
- }
63
- }
@@ -1,113 +0,0 @@
1
- const debug = require('debug')('diva:ImageCache');
2
-
3
- /* FIXME(wabain): The caching strategy here is completely
4
- * arbitrary and the implementation isn't especially efficient.
5
- */
6
- const DEFAULT_MAX_KEYS = 100;
7
-
8
- export default class ImageCache
9
- {
10
- constructor (options)
11
- {
12
- options = options || { maxKeys: DEFAULT_MAX_KEYS };
13
- this.maxKeys = options.maxKeys || DEFAULT_MAX_KEYS;
14
-
15
- this._held = {};
16
- this._urls = {};
17
- this._lru = [];
18
- }
19
-
20
- get (url)
21
- {
22
- const record = this._urls[url];
23
- return record ? record.img : null;
24
- }
25
-
26
- has (url)
27
- {
28
- return !!this._urls[url];
29
- }
30
-
31
- put (url, img)
32
- {
33
- let record = this._urls[url];
34
- if (record)
35
- {
36
- // FIXME: Does this make sense for this use case?
37
- record.img = img;
38
- this._promote(record);
39
- }
40
- else
41
- {
42
- record = {
43
- img: img,
44
- url: url
45
- };
46
-
47
- this._urls[url] = record;
48
- this._tryEvict(1);
49
- this._lru.unshift(record);
50
- }
51
- }
52
-
53
- _promote (record)
54
- {
55
- const index = this._lru.indexOf(record);
56
- this._lru.splice(index, 1);
57
- this._lru.unshift(record);
58
- }
59
-
60
- _tryEvict (extraCapacity)
61
- {
62
- const allowedEntryCount = this.maxKeys - extraCapacity;
63
-
64
- if (this._lru.length <= allowedEntryCount)
65
- return;
66
-
67
- let evictionIndex = this._lru.length - 1;
68
-
69
- for (;;)
70
- {
71
- const target = this._lru[evictionIndex];
72
-
73
- if (!this._held[target.url])
74
- {
75
- debug('Evicting image %s', target.url);
76
- this._lru.splice(evictionIndex, 1);
77
- delete this._urls[target.url];
78
-
79
- if (this._lru.length <= allowedEntryCount)
80
- break;
81
- }
82
-
83
- if (evictionIndex === 0)
84
- {
85
- /* istanbul ignore next */
86
- debug.enabled && debug('Cache overfull by %s (all entries are being held)',
87
- this._lru.length - allowedEntryCount);
88
-
89
- break;
90
- }
91
-
92
- evictionIndex--;
93
- }
94
- }
95
-
96
- acquire (url)
97
- {
98
- this._held[url] = (this._held[url] || 0) + 1;
99
- this._promote(this._urls[url]);
100
- }
101
-
102
- release (url)
103
- {
104
- const count = this._held[url];
105
-
106
- if (count > 1)
107
- this._held[url]--;
108
- else
109
- delete this._held[url];
110
-
111
- this._tryEvict(0);
112
- }
113
- }
@@ -1,157 +0,0 @@
1
- import parseIIIFManifest from './parse-iiif-manifest';
2
- import IIIFSourceAdapter from "./iiif-source-adapter";
3
-
4
-
5
- export default class ImageManifest
6
- {
7
- constructor (data, urlAdapter)
8
- {
9
- // Save all the data we need
10
- this.pages = data.pgs;
11
- this.maxZoom = data.max_zoom;
12
- this.maxRatio = data.dims.max_ratio;
13
- this.minRatio = data.dims.min_ratio;
14
- this.itemTitle = data.item_title;
15
- this.metadata = data.metadata;
16
-
17
- // Only given for IIIF manifests
18
- this.paged = !!data.paged;
19
-
20
- // These are arrays, the index corresponding to the zoom level
21
- this._maxWidths = data.dims.max_w;
22
- this._maxHeights = data.dims.max_h;
23
- this._averageWidths = data.dims.a_wid;
24
- this._averageHeights = data.dims.a_hei;
25
- this._totalHeights = data.dims.t_hei;
26
- this._totalWidths = data.dims.t_wid;
27
-
28
- this._urlAdapter = urlAdapter;
29
- }
30
-
31
- static fromIIIF (iiifManifest)
32
- {
33
- const data = parseIIIFManifest(iiifManifest);
34
- return new ImageManifest(data, new IIIFSourceAdapter());
35
- }
36
-
37
- isPageValid (pageIndex, showNonPagedPages)
38
- {
39
- if (!showNonPagedPages && this.paged && !this.pages[pageIndex].paged)
40
- {
41
- return false;
42
- }
43
-
44
- return pageIndex >= 0 && pageIndex < this.pages.length;
45
- }
46
-
47
- getMaxPageDimensions (pageIndex)
48
- {
49
- const maxDims = this.pages[pageIndex].d[this.maxZoom];
50
-
51
- return {
52
- height: maxDims.h,
53
- width: maxDims.w
54
- };
55
- }
56
-
57
- getPageDimensionsAtZoomLevel (pageIndex, zoomLevel)
58
- {
59
- const maxDims = this.pages[pageIndex].d[this.maxZoom];
60
-
61
- const scaleRatio = getScaleRatio(this.maxZoom, zoomLevel);
62
-
63
- return {
64
- height: maxDims.h * scaleRatio,
65
- width: maxDims.w * scaleRatio
66
- };
67
- }
68
-
69
- /**
70
- * Returns a URL for the image of the given page. The optional size
71
- * parameter supports setting the image width or height (default is
72
- * full-sized).
73
- */
74
- getPageImageURL (pageIndex, size)
75
- {
76
- return this._urlAdapter.getPageImageURL(this, pageIndex, size);
77
- }
78
-
79
- /**
80
- * Return an array of tile objects for the specified page and integer zoom level
81
- */
82
- getPageImageTiles (pageIndex, zoomLevel, tileDimensions)
83
- {
84
- const page = this.pages[pageIndex];
85
-
86
- if (!isFinite(zoomLevel) || zoomLevel % 1 !== 0)
87
- {
88
- throw new TypeError('Zoom level must be an integer: ' + zoomLevel);
89
- }
90
-
91
- const rows = Math.ceil(page.d[zoomLevel].h / tileDimensions.height);
92
- const cols = Math.ceil(page.d[zoomLevel].w / tileDimensions.width);
93
-
94
- const tiles = [];
95
-
96
- let row, col, url;
97
-
98
- for (row = 0; row < rows; row++)
99
- {
100
- for (col = 0; col < cols; col++)
101
- {
102
- url = this._urlAdapter.getTileImageURL(this, pageIndex, {
103
- row: row,
104
- col: col,
105
- rowCount: rows,
106
- colCount: cols,
107
- zoomLevel: zoomLevel,
108
- tileDimensions: tileDimensions
109
- });
110
-
111
- // FIXME: Dimensions should account for partial tiles (e.g. the
112
- // last row and column in a tiled image)
113
- tiles.push({
114
- row: row,
115
- col: col,
116
- zoomLevel: zoomLevel,
117
- dimensions: {
118
- height: tileDimensions.height,
119
- width: tileDimensions.width
120
- },
121
- offset: {
122
- top: row * tileDimensions.height,
123
- left: col * tileDimensions.width
124
- },
125
- url: url
126
- });
127
- }
128
- }
129
-
130
- return {
131
- zoomLevel: zoomLevel,
132
- rows: rows,
133
- cols: cols,
134
- tiles: tiles
135
- };
136
- }
137
- }
138
-
139
- ImageManifest.prototype.getMaxWidth = zoomedPropertyGetter('_maxWidths');
140
- ImageManifest.prototype.getMaxHeight = zoomedPropertyGetter('_maxHeights');
141
- ImageManifest.prototype.getAverageWidth = zoomedPropertyGetter('_averageWidths');
142
- ImageManifest.prototype.getAverageHeight = zoomedPropertyGetter('_averageHeights');
143
- ImageManifest.prototype.getTotalWidth = zoomedPropertyGetter('_totalWidths');
144
- ImageManifest.prototype.getTotalHeight = zoomedPropertyGetter('_totalHeights');
145
-
146
- function zoomedPropertyGetter (privateName)
147
- {
148
- return function (zoomLevel)
149
- {
150
- return this[privateName][zoomLevel];
151
- };
152
- }
153
-
154
- function getScaleRatio (sourceZoomLevel, targetZoomLevel)
155
- {
156
- return 1 / Math.pow(2, sourceZoomLevel - targetZoomLevel);
157
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * Handler for the request for an image tile
3
- *
4
- * @param url
5
- * @param callback
6
- * @constructor
7
- */
8
- export default class ImageRequestHandler
9
- {
10
- constructor (options)
11
- {
12
- this._url = options.url;
13
- this._callback = options.load;
14
- this._errorCallback = options.error;
15
- this.timeoutTime = options.timeoutTime || 0;
16
- this._aborted = this._complete = false;
17
-
18
- //Use a timeout to allow the requests to be debounced (as they are in renderer)
19
- this.timeout = setTimeout(() => {
20
- // Initiate the request
21
- this._image = new Image();
22
- this._image.crossOrigin = "anonymous";
23
- this._image.onload = this._handleLoad.bind(this);
24
- this._image.onerror = this._handleError.bind(this);
25
- this._image.src = options.url;
26
-
27
- }, this.timeoutTime);
28
- }
29
-
30
- abort ()
31
- {
32
- clearTimeout(this.timeout);
33
-
34
- // FIXME
35
- // People on the Internet say that doing this {{should/should not}} abort the request. I believe
36
- // it corresponds to what the WHATWG HTML spec says should happen when the UA
37
- // updates the image data if selected source is null.
38
- //
39
- // Sources:
40
- //
41
- // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element
42
- // http://stackoverflow.com/questions/7390888/does-changing-the-src-attribute-of-an-image-stop-the-image-from-downloading
43
- if (this._image)
44
- {
45
- this._image.onload = this._image.onerror = null;
46
-
47
- this._image.src = '';
48
- }
49
-
50
- this._aborted = true;
51
- }
52
-
53
- _handleLoad ()
54
- {
55
- if (this._aborted)
56
- {
57
- console.error('ImageRequestHandler invoked on cancelled request for ' + this._url);
58
- return;
59
- }
60
-
61
- if (this._complete)
62
- {
63
- console.error('ImageRequestHandler invoked on completed request for ' + this._url);
64
- return;
65
- }
66
-
67
- this._complete = true;
68
-
69
- this._callback(this._image);
70
- }
71
-
72
- _handleError ()
73
- {
74
- this._errorCallback(this._image);
75
- }
76
- }
@@ -1,122 +0,0 @@
1
- // TODO: requestAnimationFrame fallback
2
-
3
- export default {
4
- animate,
5
- easing: {
6
- linear: linearEasing,
7
- cubic: inOutCubicEasing
8
- }
9
- };
10
-
11
- let now;
12
-
13
- if (typeof performance !== 'undefined' && performance.now)
14
- {
15
- now = () => { return performance.now(); };
16
- }
17
- else
18
- {
19
- now = () => { return Date.now(); };
20
- }
21
-
22
-
23
- function animate (options)
24
- {
25
- const durationMs = options.duration;
26
- const parameters = options.parameters;
27
- const onUpdate = options.onUpdate;
28
- const onEnd = options.onEnd;
29
-
30
- // Setup
31
- // Times are in milliseconds from a basically arbitrary start
32
- const start = now();
33
- const end = start + durationMs;
34
-
35
- const tweenFns = {};
36
- const values = {};
37
- const paramKeys = Object.keys(parameters);
38
-
39
- paramKeys.forEach(key => {
40
- const config = parameters[key];
41
- tweenFns[key] = interpolate(config.from, config.to, config.easing || inOutCubicEasing);
42
- });
43
-
44
- // Run it!
45
- let requestId = requestAnimationFrame(update);
46
-
47
- return {
48
- cancel()
49
- {
50
- if (requestId !== null)
51
- {
52
- cancelAnimationFrame(requestId);
53
- handleAnimationCompletion({
54
- interrupted: true
55
- });
56
- }
57
- }
58
- };
59
-
60
- function update()
61
- {
62
- const current = now();
63
- const elapsed = Math.min((current - start) / durationMs, 1);
64
-
65
- updateValues(elapsed);
66
- onUpdate(values);
67
-
68
- if (current < end)
69
- {
70
- requestId = requestAnimationFrame(update);
71
- }
72
- else
73
- {
74
- handleAnimationCompletion({
75
- interrupted: false
76
- });
77
- }
78
- }
79
-
80
- function updateValues(elapsed)
81
- {
82
- paramKeys.forEach(key => {
83
- values[key] = tweenFns[key](elapsed);
84
- });
85
- }
86
-
87
- function handleAnimationCompletion(info)
88
- {
89
- requestId = null;
90
-
91
- if (onEnd)
92
- onEnd(info);
93
- }
94
- }
95
-
96
- function interpolate(start, end, easing)
97
- {
98
- return (elapsed) => { return start + (end - start) * easing(elapsed); };
99
- }
100
-
101
- /**
102
- * Easing functions. inOutCubicEasing is the default, but
103
- * others are given for convenience.
104
- *
105
- **/
106
- function linearEasing(e)
107
- {
108
- return e;
109
- }
110
-
111
- /* jshint ignore:start */
112
- function inOutQuadEasing (e)
113
- {
114
- return e < .5 ? 2 * e * e : -1+(4-2 * e) * e
115
- }
116
- /* jshint ignore:end */
117
-
118
-
119
- function inOutCubicEasing (t)
120
- {
121
- return t < 0.5 ? 4 * t * t * t : ( t - 1 ) * ( 2 * t - 2 ) * ( 2 * t - 2 ) + 1;
122
- }