forked from lindylearn/unclutter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransitions.ts
205 lines (167 loc) · 7.5 KB
/
transitions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import { overrideClassname } from "../common/stylesheets";
import { getDomainFrom } from "../common/util";
import AnnotationsModifier from "./modifications/annotations/annotationsModifier";
import BackgroundModifier from "./modifications/background";
import BodyStyleModifier from "./modifications/bodyStyle";
import ContentBlockModifier from "./modifications/contentBlock";
import ResponsiveStyleModifier from "./modifications/CSSOM/responsiveStyle";
import StylePatchesModifier from "./modifications/CSSOM/stylePatches";
import ThemeModifier from "./modifications/CSSOM/theme";
import CSSOMProvider from "./modifications/CSSOM/_provider";
import ReadingTimeModifier from "./modifications/DOM/readingTime";
import TextContainerModifier from "./modifications/DOM/textContainer";
import ElementPickerModifier from "./modifications/elementPicker";
import OverlayManager from "./modifications/overlay";
import {
PageModifier,
trackModifierExecution,
} from "./modifications/_interface";
import { preparePageviewAnimation } from "./pageview/enablePageView";
@trackModifierExecution
export default class TransitionManager implements PageModifier {
private domain = getDomainFrom(new URL(window.location.href));
private bodyStyleModifier = new BodyStyleModifier();
private cssomProvider = new CSSOMProvider();
private responsiveStyleModifier = new ResponsiveStyleModifier();
private stylePatchesModifier = new StylePatchesModifier(this.cssomProvider);
private annotationsModifier = new AnnotationsModifier();
private textContainerModifier = new TextContainerModifier();
private contentBlockModifier = new ContentBlockModifier(
this.domain,
this.textContainerModifier
);
private backgroundModifier = new BackgroundModifier();
private themeModifier = new ThemeModifier(
this.cssomProvider,
this.annotationsModifier,
this.textContainerModifier,
this.bodyStyleModifier
);
private elementPickerModifier = new ElementPickerModifier(this.domain);
private overlayManager = new OverlayManager(
this.domain,
this.themeModifier,
this.annotationsModifier,
this.textContainerModifier,
this.elementPickerModifier
);
private readingTimeModifier = new ReadingTimeModifier(this.overlayManager);
async prepare() {
// save original styles before changes
this.bodyStyleModifier.prepare();
// fetching CSS may take some time, so run other things in parallel
await Promise.all([
// handle CSS
(async () => {
// fetch CSS stylesheets if required
await this.cssomProvider.prepare();
// iterate CSS stylesheets
await this.responsiveStyleModifier.prepare(this.cssomProvider);
})(),
// iterate DOM
this.textContainerModifier.prepare(),
// get active theme state
this.themeModifier.prepare(this.domain),
this.elementPickerModifier.prepare(),
]);
// configure selectors
this.contentBlockModifier.prepare();
// can't set animation start properties in content.css, as that breaks some sites (e.g. xkcd.com)
preparePageviewAnimation();
}
// visually fade out noisy elements
fadeOutNoise() {
// inserts new stylesheets which trigger ~50ms reflow
this.contentBlockModifier.fadeOutNoise();
this.responsiveStyleModifier.fadeOutNoise();
}
// prepare upcoming transition
duringFadeOut() {
// order is important -- should only trigger one reflow for background insert & text baseline styles
// measure style properties for later
this.textContainerModifier.measureFontProperties();
// parse text background colors, insert background
this.textContainerModifier.fadeOutNoise();
this.backgroundModifier.fadeOutNoise();
// set background dark if dark mode enabled, configure font size variable
this.themeModifier.transitionIn();
// keep text in same position but use animatable leftMargin everywhere
// needs to be applied before transitionIn()
this.textContainerModifier.prepareAnimation();
// below steps where originally in transitionIn()
// remove faded-out elements
this.contentBlockModifier.transitionIn();
this.responsiveStyleModifier.transitionIn();
this.elementPickerModifier.transitionIn();
}
// pageview width change was triggered just before calling this
transitionIn() {
// enable site mobile styles
// this may shift layout in various ways
this.responsiveStyleModifier.enableResponsiveStyles();
// apply text container style
this.textContainerModifier.afterTransitionIn();
// adjust font size
this.textContainerModifier.setTextFontOverride();
// patch inline styles to overcome stubborn sites
// modifies DOM & CSSOM
this.bodyStyleModifier.transitionIn();
// TODO move this elsewhere if takes too much performance?
this.stylePatchesModifier.afterTransitionIn();
// to look nice, all layout shifts should be done in this phase
}
async afterTransitionIn() {
// insert iframe and wait until font loaded
this.overlayManager.createIframes();
await new Promise((r) => setTimeout(r, 50));
// show UI
// needs to be run before themeModifier to set correct auto theme value
this.overlayManager.afterTransitionIn();
// apply color theme - potentially expensive
this.themeModifier.afterTransitionIn();
await new Promise((r) => setTimeout(r, 0));
// UI enhancements, can show up later
this.annotationsModifier.afterTransitionIn(); // annotations fetch may take another 500ms
this.readingTimeModifier.afterTransitionIn();
// adjust background element height only after animations done
this.backgroundModifier.observeHeightChanges();
document.body.style.setProperty(
"transition",
"all 0.2s cubic-bezier(0.33, 1, 0.68, 1)",
"important"
);
}
async transitionOut() {
// incoming animation set via pageview class, need inline styles for outgoing animation
preparePageviewAnimation();
// setup transition for changing text margin
this.textContainerModifier.prepareTransitionOut();
// remove UI
this.annotationsModifier.transitionOut();
this.overlayManager.transitionOut();
// disable dark mode
this.themeModifier.transitionOut();
await new Promise((r) => setTimeout(r, 0));
this.bodyStyleModifier.transitionOut();
document.documentElement.classList.remove("pageview");
this.textContainerModifier.transitionOut();
// restore original layout
this.responsiveStyleModifier.transitionOut();
this.contentBlockModifier.transitionOut();
}
fadeinNoise() {
this.textContainerModifier.afterTransitionOut();
// restore noisy elements
this.contentBlockModifier.fadeInNoise();
this.responsiveStyleModifier.fadeInNoise();
}
afterTransitionOut() {
this.cssomProvider.reenableOriginalStylesheets();
// remove rest
document
.querySelectorAll(`.${overrideClassname}`)
.forEach((e) => e.remove());
// final cleanup, includes removing animation settings
this.bodyStyleModifier.afterTransitionOut();
}
}