Skip to content

Commit 64b3b03

Browse files
committed
Refactor AlbumView, fix a bug in TransformView, #52
1 parent 8c10f86 commit 64b3b03

File tree

8 files changed

+894
-358
lines changed

8 files changed

+894
-358
lines changed

components/AlbumView/AlbumSheet.js

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
// AlbumSheet.js
2+
3+
'use strict';
4+
5+
import React, {Component} from "react";
6+
import PropTypes from 'prop-types';
7+
import {StyleSheet, View, Image, Animated} from 'react-native';
8+
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
9+
10+
import Theme from 'teaset/themes/Theme';
11+
import TransformView from '../TransformView/TransformView';
12+
13+
export default class AlbumSheet extends TransformView {
14+
15+
static propTypes = {
16+
...TransformView.propTypes,
17+
image: PropTypes.oneOfType([Image.propTypes.source, PropTypes.element]).isRequired,
18+
thumb: Image.propTypes.source,
19+
defaultPosition: PropTypes.oneOf(['center', 'left', 'right']),
20+
space: PropTypes.number,
21+
load: PropTypes.bool,
22+
onWillLoadImage: PropTypes.func,
23+
onLoadImageSuccess: PropTypes.func, //(width, height)
24+
onLoadImageFailure: PropTypes.func, //(error)
25+
};
26+
27+
static defaultProps = {
28+
...TransformView.defaultProps,
29+
maxScale: 3,
30+
minScale: 1,
31+
defaultPosition: 'center',
32+
space: 20,
33+
load: true,
34+
};
35+
36+
constructor(props) {
37+
super(props);
38+
Object.assign(this.state, {
39+
position: props.defaultPosition,
40+
imageLoaded: false,
41+
thumbLoaded: false,
42+
actualWidth: 0,
43+
actualHeight: 0,
44+
fitWidth: 0,
45+
fitHeight: 0,
46+
viewWidth: 0,
47+
viewHeight: 0,
48+
});
49+
}
50+
51+
componentDidMount() {
52+
this.loadImage(this.props);
53+
}
54+
55+
componentWillReceiveProps(nextProps) {
56+
if (nextProps.image != this.props.image || nextProps.load != this.props.load) {
57+
this.loadImage(nextProps);
58+
}
59+
}
60+
61+
loadImage(props) {
62+
let {image, thumb, load, onWillLoadImage, onLoadImageSuccess, onLoadImageFailure} = props;
63+
let {imageLoaded, thumbLoaded} = this.state;
64+
65+
if (!load) return;
66+
67+
if (React.isValidElement(image)) {
68+
let {width, height} = this.getElementSize(props.image);
69+
this.imageSizeChange(width, height);
70+
this.setState({imageLoaded: true});
71+
} else {
72+
if (thumb && !thumbLoaded) {
73+
this.getImageSize(thumb, (width, height) => {
74+
if (!this.state.imageLoaded) {
75+
this.imageSizeChange(width, height);
76+
}
77+
this.setState({thumbLoaded: true});
78+
});
79+
}
80+
if (image && !imageLoaded) {
81+
onWillLoadImage && onWillLoadImage();
82+
this.getImageSize(image, (width, height) => {
83+
this.imageSizeChange(width, height);
84+
this.setState({imageLoaded: true});
85+
onLoadImageSuccess && onLoadImageSuccess(width, height);
86+
}, error => {
87+
onLoadImageFailure && onLoadImageFailure(error);
88+
});
89+
}
90+
}
91+
}
92+
93+
getImageSize(source, success, failure) {
94+
if (typeof source === 'number') {
95+
let {width, height} = resolveAssetSource(source);
96+
success && success(width, height);
97+
} else if (source && typeof source === 'object' && source.uri) {
98+
//This func will doanload and cache image
99+
Image.getSize(source.uri,
100+
(width, height) => success && success(width, height),
101+
(error) => failure && failure(error)
102+
);
103+
} else {
104+
failure && failure('source error');
105+
}
106+
}
107+
108+
getElementSize(element) {
109+
let width = 0, htight = 0;
110+
if (React.isValidElement(props.image)) {
111+
let style = StyleSheet.flatten(props.image.props.style);
112+
if (style.width === null || style.width === undefined
113+
|| style.height === null || style.height === undefined) {
114+
console.error('You need to specify the width and height style when the image is a element');
115+
} else {
116+
width = style.width;
117+
height = style.height;
118+
}
119+
}
120+
return {width, htight};
121+
}
122+
123+
getFitSize(actualWidth, actualHeight, viewWidth, viewHeight) {
124+
let fitWidth = 0, fitHeight = 0;
125+
126+
if (actualWidth && actualHeight) {
127+
fitWidth = viewWidth;
128+
fitHeight = actualHeight * fitWidth / actualWidth;
129+
if (fitHeight > viewHeight) {
130+
fitHeight = viewHeight;
131+
fitWidth = actualWidth * fitHeight / actualHeight;
132+
}
133+
} else if (actualWidth) {
134+
fitWidth = viewWidth;
135+
} else if (actualHeight) {
136+
fitHeight = viewHeight;
137+
}
138+
139+
return {fitWidth, fitHeight};
140+
}
141+
142+
getScrollValue() {
143+
let {space} = this.props;
144+
let {fitWidth, viewWidth, scale} = this.state;
145+
let scaleWidth = fitWidth * scale._value; //image scale width
146+
let exceedWidth = scaleWidth > viewWidth ? scaleWidth - viewWidth : 0;
147+
let leftX = -(viewWidth + space + exceedWidth / 2); //scroll to left position
148+
let rightX = viewWidth + space + exceedWidth / 2; //scroll to right position
149+
let centerLeftX = -exceedWidth / 2; //scroll from left to center position
150+
let centerRightX = exceedWidth / 2; //scroll from right to center position
151+
return {
152+
leftX,
153+
rightX,
154+
centerLeftX,
155+
centerRightX,
156+
};
157+
}
158+
159+
scrollTo(toPosition, animated = true, valueCallback = null) {
160+
let {position, translateX, translateY} = this.state;
161+
let {leftX, rightX, centerLeftX, centerRightX} = this.getScrollValue();
162+
163+
let valueX = 0;
164+
if (toPosition === 'left') valueX = leftX;
165+
else if (toPosition === 'right') valueX = rightX;
166+
else {
167+
if (position == 'left') valueX = centerLeftX;
168+
else valueX = centerRightX;
169+
}
170+
171+
let valueY = translateY._value;
172+
let {y, height} = this.contentLayout;
173+
if (height > this.viewLayout.height) {
174+
if (y > 0) {
175+
valueY = translateY._value - y;
176+
}
177+
else if ((y + height) < this.viewLayout.height) {
178+
valueY = translateY._value + (this.viewLayout.height - (y + height));
179+
}
180+
}
181+
182+
if (valueCallback) {
183+
valueCallback(translateX, valueX);
184+
valueCallback(translateY, valueY);
185+
} else {
186+
if (animated) {
187+
Animated.parallel([
188+
Animated.spring(translateX, {
189+
toValue: valueX,
190+
friction: 9,
191+
}),
192+
Animated.spring(translateY, {
193+
toValue: valueY,
194+
friction: 9,
195+
}),
196+
]).start();
197+
} else {
198+
translateX.setValue(valueX);
199+
translateY.setValue(valueY);
200+
}
201+
}
202+
203+
this.setState({position: toPosition});
204+
}
205+
206+
scrollX(x, animated = true) {
207+
let {position, translateX} = this.state;
208+
let {leftX, rightX} = this.getScrollValue();
209+
210+
let toValue = 0;
211+
if (position === 'left') toValue = leftX;
212+
else if (position === 'right') toValue = rightX;
213+
toValue += x;
214+
215+
if (animated) {
216+
Animated.spring(translateX, {
217+
toValue: toValue,
218+
friction: 9,
219+
}).start();
220+
} else {
221+
translateX.setValue(toValue);
222+
}
223+
}
224+
225+
restoreImage() {
226+
let {space} = this.props;
227+
let {position, viewWidth, translateX, translateY, scale} = this.state;
228+
229+
scale.setValue(1);
230+
translateY.setValue(0);
231+
switch (position) {
232+
case 'left': translateX.setValue(-(viewWidth + space)); break;
233+
case 'right': translateX.setValue(viewWidth + space); break;
234+
default: translateX.setValue(0);
235+
}
236+
}
237+
238+
layoutChange(width, height) {
239+
let {actualWidth, actualHeight, fitWidth, fitHeight, viewWidth, viewHeight} = this.state;
240+
let needRestoreImage = false;
241+
viewWidth = width;
242+
viewHeight = height;
243+
if (actualWidth && actualHeight) {
244+
let fitSize = this.getFitSize(actualWidth, actualHeight, viewWidth, viewHeight);
245+
fitWidth = fitSize.fitWidth;
246+
fitHeight = fitSize.fitHeight;
247+
needRestoreImage = true;
248+
}
249+
this.setState({actualWidth, actualHeight, fitWidth, fitHeight, viewWidth, viewHeight}, () => {
250+
needRestoreImage && this.restoreImage();
251+
});
252+
}
253+
254+
imageSizeChange(width, height) {
255+
let {actualWidth, actualHeight, fitWidth, fitHeight, viewWidth, viewHeight} = this.state;
256+
let needRestoreImage = false;
257+
actualWidth = width;
258+
actualHeight = height;
259+
if (viewWidth && viewHeight) {
260+
let fitSize = this.getFitSize(actualWidth, actualHeight, viewWidth, viewHeight);
261+
fitWidth = fitSize.fitWidth;
262+
fitHeight = fitSize.fitHeight;
263+
needRestoreImage = true;
264+
}
265+
this.setState({actualWidth, actualHeight, fitWidth, fitHeight, viewWidth, viewHeight}, () => {
266+
needRestoreImage && this.restoreImage();
267+
});
268+
}
269+
270+
buildProps() {
271+
let {style, image, thumb, load, children, onLayout, ...others} = this.props;
272+
let {position, imageLoaded, thumbLoaded, fitWidth, fitHeight} = this.state;
273+
274+
style = [{backgroundColor: 'rgba(0, 0, 0, 0)'}].concat(style);
275+
276+
let childrenStyle = {width: fitWidth, height: fitHeight};
277+
if (React.isValidElement(image)) {
278+
children = React.cloneElement(image, {style: childrenStyle});
279+
} else {
280+
if (imageLoaded || !thumb) {
281+
children = <Image style={childrenStyle} resizeMode='contain' source={image} />;
282+
} else {
283+
children = <Image style={childrenStyle} resizeMode='contain' source={thumb} />;
284+
}
285+
}
286+
287+
let saveOnLayout = onLayout;
288+
onLayout = e => {
289+
let {width, height} = e.nativeEvent.layout;
290+
this.layoutChange(width, height);
291+
saveOnLayout && saveOnLayout(e);
292+
};
293+
294+
this.props = {style, image, thumb, load, children, onLayout, ...others};
295+
super.buildProps();
296+
}
297+
298+
}

0 commit comments

Comments
 (0)