Я получаю изображение SVG с сервера.
Я использую веб-просмотр для отображения svg и задаю размер после извлечения исходных svg-измерений из файла.
У меня есть две основные функции:
1) ущипнуть, чтобы увеличить
2) увеличить конкретное место в SVG, учитывая определенные координаты
метод onPlaceHighlight позволяет мне увеличивать определенную точку, и все здесь отлично работает.
Проблема в том, когда я использую два пальца для увеличения.
Он всегда будет приближаться к центру изображения svg, в котором я поместил красный вид, который использовал для справки.
class SvgMap extends Component {
static navigationOptions = ({ navigation: { dispatch } }) => ({
title: 'Maps',
headerRight: <HeaderRight onPress={() => dispatch(navigateTo('AddFavorite'))}/>,
// headerRight: <HeaderRight/>,
})
// document.getElementById('parsed').innerHTML= typeof dataJSON.scrollByX;
//scrollTo(dataJSON.scrollByX,dataJSON.scrollByY);
//
svgContent = undefined
scaleFactor = 1
constructor(props){
super(props)
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(this.scaleFactor),
svgReady: false,
}
this.translateX = 0
this.translateY = 0
}
setScaleFactor = (scaleFactor) => {
this.scaleFactor = scaleFactor
this.state.scale.setValue(scaleFactor)
}
getScaleFactor = () => this.scaleFactor
setupPanResponder = () => {
// distance when pinch starts
let pinchDistance0 = undefined
// scale when pinch starts
let pinchScale0 = undefined
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: () => {},
onPanResponderMove: (evt, gestureState) => {
// scale.__getValue()
const scaleValue = this.getScaleFactor()
// pinch detected
if (gestureState.numberActiveTouches >= 2) {
const {
pageX: pageX0, pageY: pageY0
} = evt.nativeEvent.touches[0]
const {
pageX: pageX1, pageY: pageY1
} = evt.nativeEvent.touches[1]
const distance = Math.sqrt(
Math.pow(pageX0 - pageX1, 2) + Math.pow(pageY0 - pageY1, 2)
)
const distanceDelta = distance - pinchDistance0
// set distance0 if not set
if (pinchDistance0 === undefined) {
pinchDistance0 = distance
}
if (pinchScale0 === undefined) {
pinchScale0 = scaleValue
}
let scaleFactor = (distanceDelta * 1 / 500) + pinchScale0
if (scaleFactor < 1) {
scaleFactor = 1
} else
if (scaleFactor > 2) {
scaleFactor = 2
} else
if (!scaleFactor) {
scaleFactor = scaleValue
}
this.setScaleFactor(scaleFactor)
} else
if (gestureState.numberActiveTouches < 2) {
// reset pinch distance
pinchDistance0 = undefined
// reset pinch scale
pinchScale0 = undefined
}
const dxMax = (this.state.svgViewStyle.width * (scaleValue - 1)) / 2
const dxMin = (0 - dxMax) - (this.state.svgViewStyle.width - this.state.rootViewLayout.width)
const dyMax = (this.state.svgViewStyle.height * (scaleValue - 1)) / 2
const dyMin = (0 - dyMax) - (this.state.svgViewStyle.height - this.state.rootViewLayout.height)
let dx = this.translateX + gestureState.dx
if (dx > dxMax) {
dx = dxMax
}
if (dx < dxMin) {
dx = dxMin
}
let dy = this.translateY + gestureState.dy
if (dy > dyMax) {
dy = dyMax
}
if (dy < dyMin) {
dy = dyMin
}
console.log(dx,dy)
this.state.pan.setValue({
x: dx,
y: dy
})
},
onPanResponderRelease: (e, gestureState) => {
// reset pinch distance
pinchDistance0 = undefined
// reset pinch scale
pinchScale0 = undefined
// scale.__getValue()
const scaleValue = this.getScaleFactor()
const dxMax = (this.state.svgViewStyle.width * (scaleValue - 1)) / 2
const dxMin = (0 - dxMax) - (this.state.svgViewStyle.width - this.state.rootViewLayout.width)
const dyMax = (this.state.svgViewStyle.height * (scaleValue - 1)) / 2
const dyMin = (0 - dyMax) - (this.state.svgViewStyle.height - this.state.rootViewLayout.height)
this.translateX = this.translateX + gestureState.dx
if (this.translateX > dxMax) {
this.translateX = dxMax
}
if (this.translateX < dxMin) {
this.translateX = dxMin
}
this.translateY = this.translateY + gestureState.dy
if (this.translateY > dyMax) {
this.translateY = dyMax
}
if (this.translateY < dyMin) {
this.translateY = dyMin
}
}
})
}
onPlaceHiglight = (place) => {
console.log('onPlaceHiglight', place)
let { placeCoordinateX, placeCoordinateY } = place
if (
placeCoordinateX !== undefined && placeCoordinateX !== null &&
placeCoordinateY !== undefined && placeCoordinateY !== null
) {
if (placeCoordinateX === "") {
placeCoordinateX = 0
}
if (placeCoordinateY === "") {
placeCoordinateY = 0
}
const newScaleFactor = 1.6
Animated.timing(
this.state.scale, {
toValue: newScaleFactor,
useNativeDriver: true,
easing: Easing.linear,
duration: 300
}
).start(
() => {
this.setScaleFactor(newScaleFactor)
console.log(this.translateX,this.translateY)
const scaleValue = this.getScaleFactor()
const xMax = (this.state.svgViewStyle.width * (scaleValue - 1)) / 2
const xMin = (0 - xMax) - (this.state.svgViewStyle.width - this.state.rootViewLayout.width)
const yMax = (this.state.svgViewStyle.height * (scaleValue - 1)) / 2
const yMin = (0 - yMax) - (this.state.svgViewStyle.height - this.state.rootViewLayout.height)
// translateRatio assuming no scale
const translateRatio0 = (
this.state.svgViewStyle.width / this.state.svgViewBox.width
)
// xMax is x0
this.translateX = xMax - (
((parseInt(placeCoordinateX) * translateRatio0) * newScaleFactor)
- (this.state.rootViewLayout.width / 2)
)
// yMax is y0
this.translateY = yMax - (
((parseInt(placeCoordinateY) * translateRatio0) * newScaleFactor)
- (this.state.rootViewLayout.height / 2)
)
Animated.timing(
this.state.pan, {
toValue: {
x: this.translateX || 0,
y: this.translateY || 0
},
easing: Easing.elastic(1),
duration: 800,
useNativeDriver: true
}
).start()
}
)
}
}
componentWillMount() {
this.setupPanResponder()
axios.get(this.props.source.uri)
.then(result => {
this.svgContent = result.data
try {
const matches = String(result.data)
.match(/[.\n]*(<svg[^>]*>)[.\n]*/im)[1]
.replace("\n", "")
.match(/viewbox="([^"]*)"/i)[1].split(/\s/i)
console.log("matches",matches)
this.setState({
svgViewBox: {
x: parseInt(matches[0]),
y: parseInt(matches[1]),
width: parseInt(matches[2]),
height: parseInt(matches[3])
}
})
} catch (err) {
Alert.alert(
this.props.t('maps:error_cannot_render_correctly_title'),
this.props.t('maps:error_cannot_render_correctly'),
[
{text: 'OK', onPress: () => {}, style: 'cancel'},
],
{ cancelable: false }
)
console.error(err)
}
this.setState({
svgReady: true
})
this.props.onMapLoaded && this.props.onMapLoaded()
})
}
componentWillUpdate(nextProps, nextState) {
if (
// state changes
!_.isEqual(this.state, nextState) &&
(
// and rootViewLayout changes
!_.isEqual(
_.get(this.state, 'rootViewLayout'),
_.get(nextState, 'rootViewLayout')
) ||
// or svgViewBox changes
!_.isEqual(
_.get(this.state, 'svgViewBox'),
_.get(nextState, 'svgViewBox')
)
) &&
// and are objects both rootViewLayout and svgViewBox
_.isObject(nextState.rootViewLayout) &&
_.isObject(nextState.svgViewBox)
) {
const rootView = nextState.rootViewLayout
const svgViewBox = nextState.svgViewBox
this.setState({
svgViewStyle: {
height: rootView.height,
width: (svgViewBox.width * (rootView.height / svgViewBox.height))
}
})
}
if(
// placeHighlighted changes
_.get(this.props, 'placeHighlighted') !== _.get(nextProps, 'placeHighlighted') &&
// placeHighlighted exists and is an Object
_.isObject(_.get(nextProps, 'placeHighlighted'))
) {
this.onPlaceHiglight(_.get(nextProps, 'placeHighlighted'))
}
}
render() {
const {
styles,
source,
} = this.props
return (
<View style={[styles.root]}>
<View style={[styles.root, {
display: this.state.svgReady ? 'none' : undefined
}]}>
<View style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}}>
<Image
style={{
resizeMode: 'contain',
width: 60,
height: 60
}}
source={require('../../assets/gif/loader_inpage.gif')}
/>
</View>
</View>
<View
ref={ref => this.rootViewRef = ref}
onLayout={(event) => {
const {x, y, width, height} = event.nativeEvent.layout
this.setState({
rootViewLayout: {x, y, width, height}
})
}}
style={[styles.root, {
display: this.state.svgReady ? undefined : 'none'
}]}
{...this._panResponder.panHandlers}
>
<Animated.View
style={{
transform: [
...this.state.pan.getTranslateTransform(),
{scale: this.state.scale || 2}
],
position: 'absolute',
top: 0,
left: 0
}}
>
<View style={{
width: 50,
height: 50,
borderRadius: 25,
backgroundColor: 'red',
position: 'absolute',
zIndex:100,
top: 275,
left: 1000
}}/>
<WebView
ref={ref => this.webViewref = ref}
style={[{
height: '100%',
width: '100%'
}, this.state.svgViewStyle]}
source={this.props.source}
scalesPageToFit={true}
bounces={false}
javaScriptEnabled={false}
/>
</Animated.View>
</View>
</View>
)
}
}
Корневой стиль следующий:
root: {
flex: 1,
//backgroundColor: new AttrReference('primaryBackground').value,
backgroundColor: '#fff',
width: '100%',
height: '100%'
},
Есть предложения? Я бы хотел, чтобы щепотка масштабировалась для увеличения масштаба между двумя точками касания, не всегда в центре экрана.
Ценю любые отзывы!