-
Notifications
You must be signed in to change notification settings - Fork 0
/
view.js
257 lines (235 loc) · 8.84 KB
/
view.js
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
import paper from '@scratch/paper';
import {CROSSHAIR_SIZE, getBackgroundGuideLayer, getDragCrosshairLayer, getRaster} from './layer';
import {getAllRootItems, getSelectedRootItems} from './selection';
import {getHitBounds} from './bitmap';
import log from '../log/log';
// Vectors are imported and exported at SVG_ART_BOARD size.
// Once they are imported however, both SVGs and bitmaps are on
// canvases of ART_BOARD size.
// (This is for backwards compatibility, to handle both assets
// designed for 480 x 360, and bitmap resolution 2 bitmaps)
let SVG_ART_BOARD_WIDTH = 480;
let SVG_ART_BOARD_HEIGHT = 360;
let ART_BOARD_WIDTH = SVG_ART_BOARD_WIDTH * 2;
let ART_BOARD_HEIGHT = SVG_ART_BOARD_HEIGHT * 2;
let CENTER = new paper.Point(ART_BOARD_WIDTH / 2, ART_BOARD_HEIGHT / 2);
const PADDING_PERCENT = 25; // Padding as a percent of the max of width/height of the sprite
const BUFFER = 50; // Number of pixels of allowance around objects at the edges of the workspace
const MIN_RATIO = .125; // Zoom in to at least 1/8 of the screen. This way you don't end up incredibly
// zoomed in for tiny costumes.
// const OUTERMOST_ZOOM_LEVEL = 0.333;
const OUTERMOST_ZOOM_LEVEL = 0.05; // 原版官方缩小到最小的是 0.333(相当于 66.6%),改成 0.05(相当于 10%)
let ART_BOARD_BOUNDS = new paper.Rectangle(0, 0, ART_BOARD_WIDTH, ART_BOARD_HEIGHT);
let MAX_WORKSPACE_BOUNDS = new paper.Rectangle(
-ART_BOARD_WIDTH / 4,
-ART_BOARD_HEIGHT / 4,
ART_BOARD_WIDTH * 1.5,
ART_BOARD_HEIGHT * 1.5);
let _workspaceBounds = ART_BOARD_BOUNDS;
const getWorkspaceBounds = () => _workspaceBounds;
/**
* The workspace bounds define the areas that the scroll bars can access.
* They include at minimum the artboard, and extend to a bit beyond the
* farthest item off tne edge in any given direction (so items can't be
* "lost" off the edge)
*
* @param {boolean} clipEmpty Clip empty space from bounds, even if it
* means discontinuously jumping the viewport. This should probably be
* false unless the viewport is going to move discontinuously anyway
* (such as in a zoom button click)
*/
const setWorkspaceBounds = clipEmpty => {
const items = getAllRootItems();
// Include the artboard and what's visible in the viewport
let bounds = ART_BOARD_BOUNDS;
if (!clipEmpty) {
bounds = bounds.unite(paper.view.bounds);
}
// Include everything the user has drawn and a buffer around it
for (const item of items) {
bounds = bounds.unite(item.bounds.expand(BUFFER));
}
// Limit to max workspace bounds
bounds = bounds.intersect(MAX_WORKSPACE_BOUNDS.expand(BUFFER));
let top = bounds.top;
let left = bounds.left;
let bottom = bounds.bottom;
let right = bounds.right;
// Center in view if viewport is larger than workspace
let hDiff = 0;
let vDiff = 0;
if (bounds.width < paper.view.bounds.width) {
hDiff = (paper.view.bounds.width - bounds.width) / 2;
left -= hDiff;
right += hDiff;
}
if (bounds.height < paper.view.bounds.height) {
vDiff = (paper.view.bounds.height - bounds.height) / 2;
top -= vDiff;
bottom += vDiff;
}
_workspaceBounds = new paper.Rectangle(left, top, right - left, bottom - top);
};
const clampViewBounds = () => {
const {left, right, top, bottom} = paper.project.view.bounds;
if (left < _workspaceBounds.left) {
paper.project.view.scrollBy(new paper.Point(_workspaceBounds.left - left, 0));
}
if (top < _workspaceBounds.top) {
paper.project.view.scrollBy(new paper.Point(0, _workspaceBounds.top - top));
}
if (bottom > _workspaceBounds.bottom) {
paper.project.view.scrollBy(new paper.Point(0, _workspaceBounds.bottom - bottom));
}
if (right > _workspaceBounds.right) {
paper.project.view.scrollBy(new paper.Point(_workspaceBounds.right - right, 0));
}
setWorkspaceBounds();
};
const resizeCrosshair = () => {
if (getDragCrosshairLayer() && getDragCrosshairLayer().dragCrosshair) {
getDragCrosshairLayer().dragCrosshair.scale(
CROSSHAIR_SIZE / getDragCrosshairLayer().dragCrosshair.bounds.width / paper.view.zoom);
}
if (getBackgroundGuideLayer() && getBackgroundGuideLayer().dragCrosshair) {
getBackgroundGuideLayer().dragCrosshair.scale(
CROSSHAIR_SIZE / getBackgroundGuideLayer().dragCrosshair.bounds.width / paper.view.zoom);
}
};
// Zoom keeping a project-space point fixed.
// This article was helpful http://matthiasberth.com/tech/stable-zoom-and-pan-in-paperjs
const zoomOnFixedPoint = (deltaZoom, fixedPoint) => {
const view = paper.view;
const preZoomCenter = view.center;
const newZoom = Math.max(OUTERMOST_ZOOM_LEVEL, view.zoom + deltaZoom);
const scaling = view.zoom / newZoom;
const preZoomOffset = fixedPoint.subtract(preZoomCenter);
const postZoomOffset = fixedPoint.subtract(preZoomOffset.multiply(scaling))
.subtract(preZoomCenter);
view.zoom = newZoom;
view.translate(postZoomOffset.multiply(-1));
setWorkspaceBounds(true /* clipEmpty */);
clampViewBounds();
resizeCrosshair();
};
// Zoom keeping the selection center (if any) fixed.
const zoomOnSelection = deltaZoom => {
let fixedPoint;
const items = getSelectedRootItems();
if (items.length > 0) {
let rect = null;
for (const item of items) {
if (rect) {
rect = rect.unite(item.bounds);
} else {
rect = item.bounds;
}
}
fixedPoint = rect.center;
} else {
fixedPoint = paper.project.view.center;
}
zoomOnFixedPoint(deltaZoom, fixedPoint);
};
const resetZoom = () => {
paper.project.view.zoom = .5;
setWorkspaceBounds(true /* clipEmpty */);
resizeCrosshair();
clampViewBounds();
};
const pan = (dx, dy) => {
paper.project.view.scrollBy(new paper.Point(dx, dy));
clampViewBounds();
};
/**
* Mouse actions are clamped to action bounds
* @param {boolean} isBitmap True if the editor is in bitmap mode, false if it is in vector mode
* @returns {paper.Rectangle} the bounds within which mouse events should work in the paint editor
*/
const getActionBounds = isBitmap => {
if (isBitmap) {
return ART_BOARD_BOUNDS;
}
return paper.view.bounds.unite(ART_BOARD_BOUNDS).intersect(MAX_WORKSPACE_BOUNDS);
};
const zoomToFit = isBitmap => {
resetZoom();
let bounds;
if (isBitmap) {
bounds = getHitBounds(getRaster()).expand(BUFFER);
} else {
const items = getAllRootItems();
for (const item of items) {
if (bounds) {
bounds = bounds.unite(item.bounds);
} else {
bounds = item.bounds;
}
}
}
if (bounds && bounds.width && bounds.height) {
const canvas = paper.view.element;
// Ratio of (sprite length plus padding on all sides) to viewport length.
let ratio = paper.view.zoom *
Math.max(
bounds.width * (1 + (2 * PADDING_PERCENT / 100)) / canvas.clientWidth,
bounds.height * (1 + (2 * PADDING_PERCENT / 100)) / canvas.clientHeight);
// Clamp ratio
ratio = Math.max(Math.min(1, ratio), MIN_RATIO);
if (ratio < 1) {
paper.view.center = bounds.center;
paper.view.zoom = paper.view.zoom / ratio;
resizeCrosshair();
clampViewBounds();
}
} else {
log.warn('No bounds!');
}
};
/**
* 调整 SVG_ART_BOARD_WIDTH、SVG_ART_BOARD_HEIGHT 数值后,重新设置相关的变量
*/
const reset = () => {
ART_BOARD_WIDTH = SVG_ART_BOARD_WIDTH * 2;
ART_BOARD_HEIGHT = SVG_ART_BOARD_HEIGHT * 2;
CENTER = new paper.Point(ART_BOARD_WIDTH / 2, ART_BOARD_HEIGHT / 2);
ART_BOARD_BOUNDS = new paper.Rectangle(0, 0, ART_BOARD_WIDTH, ART_BOARD_HEIGHT);
MAX_WORKSPACE_BOUNDS = new paper.Rectangle(
-ART_BOARD_WIDTH / 4,
-ART_BOARD_HEIGHT / 4,
ART_BOARD_WIDTH * 1.5,
ART_BOARD_HEIGHT * 1.5);
_workspaceBounds = ART_BOARD_BOUNDS;
}
/**
* 设置 paint 可编辑区域的宽高,并且重置与其相关的变量
* PS:这种做法不算很好的设计,只是暂时可以实现功能,后期看有没有更好的方案优化优化
* @param {number} width 可编辑区域的宽度
* @param {number} height 可编辑区域的高度
*/
const setSvgArtBoardWidthHeight = (width, height) => {
SVG_ART_BOARD_WIDTH = width;
SVG_ART_BOARD_HEIGHT = height;
reset();
}
export {
ART_BOARD_BOUNDS,
ART_BOARD_HEIGHT,
ART_BOARD_WIDTH,
CENTER,
OUTERMOST_ZOOM_LEVEL,
SVG_ART_BOARD_WIDTH,
SVG_ART_BOARD_HEIGHT,
MAX_WORKSPACE_BOUNDS,
clampViewBounds,
getActionBounds,
pan,
resetZoom,
setWorkspaceBounds,
getWorkspaceBounds,
resizeCrosshair,
zoomOnSelection,
zoomOnFixedPoint,
zoomToFit,
setSvgArtBoardWidthHeight,
};