Browse Source

add graph and about pages

master
Milan Pässler 5 months ago
parent
commit
765a6bd216
13 changed files with 1630 additions and 90 deletions
  1. 8
    2
      js/ai.js
  2. 1268
    0
      js/d3-force.js
  3. 49
    0
      js/image.js
  4. 5
    3
      js/main.js
  5. 6
    13
      js/routes/about.js
  6. 0
    17
      js/routes/feed.js
  7. 4
    49
      js/routes/gallery.js
  8. 207
    0
      js/routes/graph.js
  9. 30
    0
      js/routes/start.js
  10. 39
    0
      js/storage.js
  11. 9
    0
      js/util.js
  12. 4
    5
      main.css
  13. 1
    1
      sw.js

+ 8
- 2
js/ai.js View File

@@ -1,5 +1,7 @@
1 1
 'use strict';
2 2
 
3
+import { addObjects, getNextId, getGraph } from './storage.js';
4
+
3 5
 //
4 6
 // Initial configuration
5 7
 //
@@ -112,7 +114,8 @@ class Rectangle extends Shape {
112 114
 }
113 115
 
114 116
 class Objects {
115
-	constructor(objects) {
117
+	constructor(objects, id) {
118
+		this.id = id || getNextId();
116 119
 		if (objects) {
117 120
 			this.objects = objects;
118 121
 		} else {
@@ -125,7 +128,7 @@ class Objects {
125 128
 	}
126 129
 
127 130
 	clone() {
128
-		return new Objects(this.objects.map(obj => obj.clone()));
131
+		return new Objects(this.objects.map(obj => obj.clone()), this.id);
129 132
 	}
130 133
 
131 134
 	addRandom() {
@@ -147,6 +150,7 @@ class Objects {
147 150
 	}
148 151
 
149 152
 	mutate() {
153
+		this.id = getNextId();
150 154
 		for (let obj of this.objects) if (Math.random() < MUTA_OBJ_MUT) obj.mutate();
151 155
 		if (Math.random() < MUTA_OBJ_ADD) this.addRandom();
152 156
 		if (Math.random() < MUTA_OBJ_REM) this.removeRandom();
@@ -160,6 +164,8 @@ export const next = (cur) => {
160 164
 		newObjects[i] = cur.clone();
161 165
 		if (i > 0) newObjects[i].mutate();
162 166
 	}
167
+	addObjects(newObjects, cur);
168
+	console.log(getGraph());
163 169
 	return newObjects;
164 170
 };
165 171
 

+ 1268
- 0
js/d3-force.js
File diff suppressed because it is too large
View File


+ 49
- 0
js/image.js View File

@@ -0,0 +1,49 @@
1
+'use strict';
2
+
3
+export const size = 128;
4
+export const renderImage = (objects) => {
5
+	const canvas = document.createElement('canvas');
6
+	const ctx = canvas.getContext('2d');
7
+	canvas.height = size;
8
+	canvas.width = size;
9
+
10
+	ctx.clearRect(0, 0, size, size);
11
+	objects.objects.forEach((obj) => {
12
+		let first = obj['points'].shift();
13
+		let x = first['x'] * size;
14
+		let y = first['y'] * size;
15
+		let r = Math.round(first['r']*255);
16
+		let g = Math.round(first['g']*255);
17
+		let b = Math.round(first['b']*255);
18
+		ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`;
19
+		ctx.moveTo(x, y);
20
+		ctx.beginPath();
21
+		if (obj['radius']) {
22
+			let radius = obj['radius'] * size;
23
+			if (radius < 0) {
24
+				console.warn('oops, radius was < 0: ' + radius);
25
+			} else {
26
+				ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
27
+			}
28
+		} else {
29
+			let second = obj['points'][0];
30
+			let x2 = second['x'] * size;
31
+			let y2 = second['y'] * size;
32
+			let r2 = Math.round(second['r']*255);
33
+			let g2 = Math.round(second['g']*255);
34
+			let b2 = Math.round(second['b']*255);
35
+			let grd = ctx.createLinearGradient(x, y, x2, y2);
36
+			grd.addColorStop(0, `rgba(${r}, ${g}, ${b}, 1)`);
37
+			grd.addColorStop(1, `rgba(${r2}, ${g2}, ${b2}, 1)`);
38
+			ctx.fillStyle = grd;
39
+			obj['points'].forEach((point) => {
40
+				ctx.lineTo(point['x'] * size, point['y'] * size);
41
+			});
42
+		}
43
+		ctx.closePath();
44
+		ctx.fill();
45
+	});
46
+
47
+	return canvas;
48
+};
49
+

+ 5
- 3
js/main.js View File

@@ -4,8 +4,9 @@ import { route, go } from './router.js';
4 4
 import { tag } from './util.js';
5 5
 import { html, render } from './lit-html.js';
6 6
 
7
+import { startRoute } from './routes/start.js';
7 8
 import { aboutRoute } from './routes/about.js';
8
-import { feedRoute } from './routes/feed.js';
9
+import { graphRoute } from './routes/graph.js';
9 10
 import { galleryRoute } from './routes/gallery.js';
10 11
 import { notfoundRoute } from './routes/notfound.js';
11 12
 
@@ -40,8 +41,9 @@ export const headerTemplate = () => html`
40 41
 `;
41 42
 render(headerTemplate(), tag('header'));
42 43
 
43
-route(/^\/$/, aboutRoute);
44
-route(/^\/feed$/, feedRoute);
44
+route(/^\/$/, startRoute);
45
+route(/^\/about$/, aboutRoute);
46
+route(/^\/graph$/, graphRoute);
45 47
 route(/^\/gallery$/, galleryRoute);
46 48
 route(/^.*$/, notfoundRoute);
47 49
 

+ 6
- 13
js/routes/about.js View File

@@ -4,20 +4,13 @@ import { html, render } from '../lit-html.js';
4 4
 import { tag } from '../util.js';
5 5
 
6 6
 const aboutTemplate = (error) => html`
7
-	<div class="container about">
7
+	<div class="container">
8 8
 		<section>
9
-			<a href="#/account">
10
-				<h3>Sign up</h3>
11
-				<p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum. </p>
12
-			</a>
13
-			<a href="#/gallery">
14
-				<h3>Gallery</h3>
15
-				<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p>
16
-			</a>
17
-			<a href="#/feed">
18
-				<h3>Feed</h3>
19
-				<p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium. </p>
20
-			</a>
9
+			<div>
10
+				<h3>About deepart</h3>
11
+				<p>This project was developed by <a href="https://twitter.com/fronbasal/">@fronbasal</a>, <a href="https://twitter.com/StoneLabs__">@StoneLabs__</a> and <a href="https://twitter.com/petabyteboy/">@petabyteboy</a> for the <a href="https://opencodes.io/">OpenCodes 2018 hackathon</a> and won the "Art & Technology" category. It shines a light on the beauty of abstract art by letting the user choose from a selection of pictures to create an esthetically pleasing result.</p>
12
+				<p>The source code of the implementation is available at <a href="https://git.pbb.lc/petabyteboy/deepart.ai/">https://git.pbb.lc/petabyteboy/deepart.ai/</a> under the AGPL-3.0 License.</p>
13
+			</div>
21 14
 		</section>
22 15
 	</div>
23 16
 `;

+ 0
- 17
js/routes/feed.js View File

@@ -1,17 +0,0 @@
1
-'use strict';
2
-
3
-import { html, render } from '../lit-html.js';
4
-import { tag } from '../util.js';
5
-
6
-const feedTemplate = (images) => html`
7
-	${images.map(image => html`
8
-		<img id="image-${image.id}" src="${image.url}">
9
-	`)}
10
-`;
11
-
12
-export const feedRoute = () => {
13
-	const notes = JSON.parse(localStorage.getItem('notes') || "[]")
14
-	render(feedTemplate(notes), tag('main'));
15
-};
16
-
17
-

+ 4
- 49
js/routes/gallery.js View File

@@ -3,12 +3,7 @@
3 3
 import { html, render } from '../lit-html.js';
4 4
 import { tag, cssClass, selAll, id, sleep } from '../util.js';
5 5
 import { next } from '../ai.js';
6
-
7
-const size = 128;
8
-const canvas = document.createElement('canvas');
9
-const ctx = canvas.getContext('2d');
10
-canvas.height = size;
11
-canvas.width = size;
6
+import { renderImage } from '../image.js';
12 7
 
13 8
 const galleryTemplate = (images, loading) => html`
14 9
 	<div class="container gallery">
@@ -40,58 +35,18 @@ const galleryTemplate = (images, loading) => html`
40 35
 	</div>
41 36
 `;
42 37
 
43
-const renderImage = (objects) => {
44
-	ctx.clearRect(0, 0, size, size);
45
-	objects.objects.forEach((obj) => {
46
-		let first = obj['points'].shift();
47
-		let x = first['x'] * size;
48
-		let y = first['y'] * size;
49
-		let r = Math.round(first['r']*255);
50
-		let g = Math.round(first['g']*255);
51
-		let b = Math.round(first['b']*255);
52
-		ctx.fillStyle = `rgba(${r}, ${g}, ${b}, 1)`;
53
-		ctx.moveTo(x, y);
54
-		ctx.beginPath();
55
-		if (obj['radius']) {
56
-			let radius = obj['radius'] * size;
57
-			if (radius < 0) {
58
-				console.warn('oops, radius was < 0: ' + radius);
59
-			} else {
60
-				ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
61
-			}
62
-		} else {
63
-			let second = obj['points'][0];
64
-			let x2 = second['x'] * size;
65
-			let y2 = second['y'] * size;
66
-			let r2 = Math.round(second['r']*255);
67
-			let g2 = Math.round(second['g']*255);
68
-			let b2 = Math.round(second['b']*255);
69
-			let grd = ctx.createLinearGradient(x, y, x2, y2);
70
-			grd.addColorStop(0, `rgba(${r}, ${g}, ${b}, 1)`);
71
-			grd.addColorStop(1, `rgba(${r2}, ${g2}, ${b2}, 1)`);
72
-			ctx.fillStyle = grd;
73
-			obj['points'].forEach((point) => {
74
-				ctx.lineTo(point['x'] * size, point['y'] * size);
75
-			});
76
-		}
77
-		ctx.closePath();
78
-		ctx.fill();
79
-	});
80
-	return canvas.toDataURL("image/png");
81
-};
82
-
83 38
 export const galleryRoute = async () => {
84
-	let data = next();
39
+	let data;
85 40
 
86 41
 	const nextSet = (selected) => {
87 42
 		render(galleryTemplate([], true), tag('main'));
88 43
 
89
-		data = next(data[selected || 0].clone());
44
+		data = next(data ? data[selected || 0].clone() : null);
90 45
 
91 46
 		let images = Object.keys(data).map(index => {
92 47
 			return {
93 48
 				id: index,
94
-				url: renderImage(data[index].clone())
49
+				url: renderImage(data[index].clone()).toDataURL("image/png")
95 50
 			};
96 51
 		});
97 52
 

+ 207
- 0
js/routes/graph.js View File

@@ -0,0 +1,207 @@
1
+'use strict';
2
+
3
+import { html, render } from '../lit-html.js';
4
+import * as d3Force from '../d3-force.js';
5
+import { tag } from '../util.js';
6
+import { getGraph } from '../storage.js';
7
+import { renderImage, size } from '../image.js';
8
+
9
+const WEIGHT = 8;
10
+const dpr = window.devicePixelRatio || 1;
11
+let canvas, ctx, graph = { nodes: [], links: [] }, simulation;
12
+
13
+const view = {
14
+    offsetX: 0,
15
+    offsetY: 0,
16
+    scale: 1,
17
+    dragging: false,
18
+    clicking: false,
19
+    pinching: false,
20
+    dragStartMouseX: 0,
21
+    dragStartMouseY: 0,
22
+    dragStartOffsetX: 0,
23
+    dragStartOffsetY: 0,
24
+    pinchStartDistance: 0,
25
+    pinchStartScale: 0
26
+};
27
+
28
+
29
+const graphTemplate = () => html`
30
+	<canvas></canvas>
31
+`;
32
+
33
+export const graphRoute = () => {
34
+	render(graphTemplate(), tag('main'));
35
+	canvas = tag('canvas');
36
+	ctx = canvas.getContext('2d');
37
+	handleResize();
38
+
39
+	const graphData = getGraph();
40
+console.log(graphData);
41
+	graph = {
42
+		nodes: Object.keys(graphData.nodes).map(i => {
43
+			const node = graphData.nodes[i];
44
+			return {
45
+				...node,
46
+				img: renderImage(node),
47
+				fy: -(graphData.currentSetId - node.setId) * 30 + 50
48
+			};
49
+		}),
50
+		links: Object.keys(graphData.links).map(i => graphData.links[i])
51
+	};
52
+
53
+	simulation = d3Force.forceSimulation()
54
+		.nodes(graph.nodes)
55
+		.force("link", d3Force.forceLink(graph.links).id(d => d.id).strength(0.1).distance(50))
56
+		.force("collide", d3Force.forceCollide().radius(WEIGHT / 2))
57
+		.force("center", d3Force.forceCenter());
58
+	drawGraph();
59
+};
60
+
61
+const x = (p) => {
62
+	return Math.floor((p + view.offsetX) * view.scale * dpr + canvas.width / 2);
63
+};
64
+
65
+const y = (p) => {
66
+	return Math.floor((p + view.offsetY) * view.scale * dpr + canvas.height / 2);
67
+};
68
+
69
+const drawGraph = () => {
70
+	ctx.clearRect(0, 0, canvas.width, canvas.height);
71
+
72
+	for (let link of graph.links) {
73
+
74
+		if (link.source === view.selectedNode) {
75
+			ctx.strokeStyle = "rgba(255, 50, 0, .5)";
76
+			ctx.lineWidth = 5;
77
+		} else if (link.target === view.selectedNode) {
78
+			ctx.strokeStyle = "rgba(0, 50, 255, .5)";
79
+			ctx.lineWidth = 5;
80
+		} else {
81
+			ctx.strokeStyle = "rgba(255, 255, 255, .1)";
82
+			ctx.lineWidth = 1;
83
+		}
84
+
85
+		ctx.beginPath();
86
+		ctx.moveTo(x(link.source.x) + (WEIGHT * view.scale * dpr) / 2, y(link.source.y) + (WEIGHT * view.scale * dpr) / 2);
87
+		ctx.lineTo(x(link.target.x) + (WEIGHT * view.scale * dpr) / 2, y(link.target.y) + (WEIGHT * view.scale * dpr) / 2);
88
+		ctx.stroke();
89
+	}
90
+
91
+	for (let node of graph.nodes) {
92
+		if (node.img) {
93
+			ctx.drawImage(node.img, x(node.x), y(node.y), WEIGHT * view.scale * dpr, WEIGHT * view.scale * dpr)
94
+		} else {
95
+			ctx.fillStyle = node === view.selectedNode ? "yellow" : "red";
96
+			ctx.fillRect(x(node.x), y(node.y), WEIGHT * view.scale * dpr, WEIGHT * view.scale * dpr);
97
+		}
98
+		ctx.fillStyle = "white";
99
+	}
100
+
101
+	if (window.location.hash === '#/graph') window.requestAnimationFrame(drawGraph);
102
+};
103
+
104
+const handleResize = () =>  {
105
+        const headerRect = tag('header').getBoundingClientRect();
106
+
107
+        canvas.width = (window.innerWidth) * dpr;
108
+        canvas.height = (window.innerHeight - headerRect.height - 2) * dpr;
109
+
110
+	ctx.restore();
111
+        ctx.save();
112
+        ctx.scale(dpr, dpr);
113
+}
114
+
115
+const nodeAt = (xPos, yPos) =>  {
116
+	return graph.nodes.filter(n => x(n.x) <= xPos && y(n.y) <= yPos
117
+		&& x(n.x + WEIGHT) >= xPos && y(n.y + WEIGHT) >= yPos)[0];
118
+}
119
+
120
+const handleClick = (xPos, yPos) =>  {
121
+	const node = nodeAt(xPos, yPos);
122
+	if (!node) view.selectedNode = undefined;
123
+	else view.selectedNode = node;
124
+}
125
+
126
+window.addEventListener('wheel', function(evt) {
127
+	if (window.location.hash !== '#/graph') return;
128
+	if (view.scale < evt.deltaY/60) return;
129
+	view.scale -= evt.deltaY/60;
130
+});
131
+
132
+const pinchDistance = (touchEvent) =>  {
133
+	return Math.abs(touchEvent.touches[0].pageX - touchEvent.touches[1].pageX) + Math.abs(touchEvent.touches[0].pageY - touchEvent.touches[1].pageY);
134
+}
135
+
136
+const mouseDownHandler = (x, y, touchEvent) =>  {
137
+	if (window.location.hash !== '#/graph') return;
138
+	if (touchEvent && touchEvent.touches.length >= 2) {
139
+		view.pinching = true;
140
+		view.pinchStartDistance = pinchDistance(touchEvent);
141
+		view.pinchStartScale = view.scale;
142
+	}
143
+
144
+	view.dragging = true;
145
+	view.clicking = true;
146
+	view.dragStartMouseX = x;
147
+	view.dragStartMouseY = y;
148
+	view.draggedNode = nodeAt(x, y);
149
+	console.log(view);
150
+	if (view.draggedNode) {
151
+		view.dragStartOffsetX = view.draggedNode.x;
152
+		view.dragStartOffsetY = view.draggedNode.y;
153
+		simulation.restart();
154
+	} else {
155
+		view.dragStartOffsetX = view.offsetX;
156
+		view.dragStartOffsetY = view.offsetY;
157
+	}
158
+}
159
+
160
+const mouseMoveHandler = (x, y, touchEvent) =>  {
161
+	if (window.location.hash !== '#/graph') return;
162
+	if (touchEvent && view.pinching) {
163
+		view.scale = view.pinchStartScale * (pinchDistance(touchEvent) / view.pinchStartDistance);
164
+	}
165
+	if (view.dragging) {
166
+		if (Math.abs(view.dragStartMouseX - x) + Math.abs(view.dragStartMouseY - y) > 10)
167
+			view.clicking = false;
168
+
169
+		if (view.draggedNode) {
170
+			view.draggedNode.fx = view.dragStartOffsetX - (view.dragStartMouseX - x) / view.scale;
171
+			if (!view.draggedNode.originalFy) view.draggedNode.originalFy = view.draggedNode.fy;
172
+			view.draggedNode.fy = view.dragStartOffsetY - (view.dragStartMouseY - y) / view.scale;
173
+			simulation.alpha(0.5);
174
+		} else {
175
+			view.offsetX = view.dragStartOffsetX - (view.dragStartMouseX - x) / view.scale;
176
+			view.offsetY = view.dragStartOffsetY - (view.dragStartMouseY - y) / view.scale;
177
+		}
178
+	}
179
+}
180
+
181
+const mouseUpHandler = (x, y, touchEvent) =>  {
182
+	if (window.location.hash !== '#/graph') return;
183
+	if (touchEvent) {
184
+		view.dragging = touchEvent.touches.length >= 1;
185
+		view.pinching = touchEvent.touches.length >= 2;
186
+		if (view.dragging) return;
187
+	}
188
+
189
+	view.dragging = false;
190
+
191
+	if (view.draggedNode) {
192
+		view.draggedNode.fx = undefined;
193
+		view.draggedNode.fy = view.draggedNode.originalFy;
194
+		view.draggedNode.originalFy = undefined;
195
+		view.draggedNode = undefined;
196
+	}
197
+
198
+	if (view.clicking) handleClick(x, y);
199
+}
200
+
201
+window.addEventListener("touchstart", (evt) => mouseDownHandler(evt.touches[0].pageX, evt.touches[0].pageY, evt));
202
+window.addEventListener("touchmove", (evt) => mouseMoveHandler(evt.touches[0].pageX, evt.touches[0].pageY, evt));
203
+window.addEventListener("touchend", (evt) => mouseUpHandler(evt.changedTouches[0].pageX*dpr, evt.changedTouches[0].pageY*dpr, evt));
204
+window.addEventListener("mousedown", (evt) => mouseDownHandler(evt.pageX, evt.pageY));
205
+window.addEventListener("mousemove", (evt) => mouseMoveHandler(evt.pageX, evt.pageY));
206
+window.addEventListener("mouseup", (evt) => mouseUpHandler(evt.pageX, evt.pageY));
207
+window.addEventListener("resize", handleResize);

+ 30
- 0
js/routes/start.js View File

@@ -0,0 +1,30 @@
1
+'use strict';
2
+
3
+import { html, render } from '../lit-html.js';
4
+import { tag } from '../util.js';
5
+
6
+const startTemplate = (error) => html`
7
+	<div class="container start">
8
+		<section>
9
+			<a href="#/about">
10
+				<h3>About</h3>
11
+				<p>Find more information on the project, its scope and its authors.</p>
12
+			</a>
13
+			<a href="#/gallery">
14
+				<h3>Gallery view</h3>
15
+				<p>Create new artworks by selecting your favorite mutation from the current iteration.</p>
16
+			</a>
17
+			<a href="#/graph">
18
+				<h3>Graph view</h3>
19
+				<p>See the last iterations of the artwork in a force-simulated graph.</p>
20
+			</a>
21
+		</section>
22
+	</div>
23
+`;
24
+
25
+export const startRoute = () => {
26
+	render(startTemplate(), tag('main'));
27
+	return true;
28
+};
29
+
30
+

+ 39
- 0
js/storage.js View File

@@ -0,0 +1,39 @@
1
+'use strict';
2
+
3
+import { filterObject } from './util.js';
4
+
5
+export const getNextId = () => {
6
+	const nextId = Number(localStorage.getItem("nextId")) || 1;
7
+	localStorage.setItem("nextId", nextId + 1);
8
+	return nextId;
9
+};
10
+
11
+const getNextSetId = () => {
12
+	const nextSetId = Number(localStorage.getItem("nextSetId")) || 1;
13
+	localStorage.setItem("nextSetId", nextSetId + 1);
14
+	return nextSetId;
15
+};
16
+
17
+export const setGraph = (newData) => localStorage.setItem("graph", JSON.stringify(newData));
18
+
19
+export const getGraph = () => JSON.parse(localStorage.getItem("graph") || '{"nodes":{},"links":{}}');
20
+
21
+export const addObjects = (nodes, previous) => {
22
+	const graph = getGraph();
23
+	const setId = getNextSetId();
24
+	graph.currentSetId = setId;
25
+
26
+	// delete old sets
27
+	graph.nodes = filterObject(graph.nodes, node => node.setId > setId - 20);
28
+	graph.links = filterObject(graph.links, link => graph.nodes[link.source] && graph.nodes[link.target]);
29
+
30
+	for (let node of nodes) {
31
+		if (graph.nodes[node.id]) continue;
32
+		graph.nodes[node.id] = {...node, setId: previous.id == node.id ? setId - 1 : setId};
33
+		graph.links[previous.id + '-' + node.id] = {
34
+			source: previous.id,
35
+			target: node.id
36
+		};
37
+	}
38
+	setGraph(graph);
39
+};

+ 9
- 0
js/util.js View File

@@ -11,3 +11,12 @@ export const cssClassAll = (className) => document.getElementsByClassName(classN
11 11
 export const sleep = (duration) => new Promise((resolve) => {
12 12
 	setTimeout(resolve, duration);
13 13
 });
14
+
15
+export const filterObject = (obj, predicate) => {
16
+	return Object.keys(obj)
17
+		.filter(key => predicate(obj[key]))
18
+		.reduce((res, key) => {
19
+			res[key] = obj[key];
20
+			return res;
21
+		}, {});
22
+};

+ 4
- 5
main.css View File

@@ -77,7 +77,7 @@ a {
77 77
 	color: white;
78 78
 }
79 79
 
80
-a h3 {
80
+h3 {
81 81
 	color: #f44336;
82 82
 }
83 83
 
@@ -85,11 +85,11 @@ a:hover {
85 85
 	text-decoration: underline;
86 86
 }
87 87
 
88
-.container.about {
88
+/*.container.start {
89 89
 	text-align: center;
90
-}
90
+}*/
91 91
 
92
-.container.about a {
92
+.container.start a {
93 93
 	margin: 0 1em;
94 94
 }
95 95
 
@@ -102,7 +102,6 @@ header, .nav {
102 102
 	background: rgba(255, 255, 255, .1);
103 103
 }
104 104
 
105
-
106 105
 header>.container {
107 106
 	display: flex;
108 107
 	align-items: center;

+ 1
- 1
sw.js View File

@@ -1,6 +1,6 @@
1 1
 'use strict';
2 2
 
3
-const CACHE = 'cache-v50';
3
+const CACHE = 'cache-v51';
4 4
 
5 5
 let preCache = [
6 6
 	'./',

Loading…
Cancel
Save