software-mansion / react-native-svg

SVG library for React Native, React Native Web, and plain React web projects.
MIT License
7.5k stars 1.13k forks source link

How to put image inside polygon #1111

Closed vladakg85 closed 5 years ago

vladakg85 commented 5 years ago

I have a list of polygons and in each I am trying to put image. But whatever I try image never use coordinates of the polygon but use coordinates of the screen. This is example of the problem. Image should be in red polygon and use it's x:0,y:0.

Screen Shot 2019-09-19 at 21 47 48
<Svg width="446px" height="694px" viewBox="0 0 546 794" version="1.1" >
    <G id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <G id="main" transform="translate(1.000000, 1.000000)" fill="#EDAF51" fill-rule="nonzero" stroke="#720D00">
<G>
                <Polygon fill=#f00" id="XS-03" points="169.90269 90.942578...">
                </Polygon>
                <Image 
                    x="0" 
                    y="0" 
                    width="45" 
                    height="45" 
                    href={require('../../../images/soldier.png')} />
            </G>
...
vladakg85 commented 5 years ago

From some reason image is relative to whole screen and not to the which is in. Any idea?

msand commented 5 years ago

The first point in your polygon is the origin you should use. You can't reference it in any other way. You could place the image in a pattern, and fill using that.

Atuldhaka commented 5 years ago

Hey, guys facing the same issue please can you explain me a bit more about this.

msand commented 5 years ago

Please specify more clearly what you're looking for

Atuldhaka commented 5 years ago

i m trying too find the centroid of a polygon which is provided from the library react-native-svg(for drawing polygon) . on that centroid i need to draw a marker.

sample code : -

Alert.alert("tapped on the polygon")} />
Atuldhaka commented 5 years ago

`

Alert.alert("tapped on the polygon")} /> `
Atuldhaka commented 5 years ago

Please specify more clearly what you're looking for

like i need to have the marker drawn over the centroid of the polygon, which i draws from the react-native-svg, so do you have any solutions regarding this.

msand commented 5 years ago

You mean like this? https://snack.expo.io/HJHxWLe3B https://en.wikipedia.org/wiki/Centroid#Balancing_method

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Svg, Polygon, Circle } from 'react-native-svg';
import Constants from 'expo-constants';

export default class App extends React.Component {
  render() {
    const points = '40,5 70,80 25,95';
    const ps = points.split(' ').map(p => p.split(',').map(Number));
    const sum = ps.reduce(([x0, y0], [x1, y1]) => [x0 + x1, y0 + y1]);
    const [x, y] = sum;
    const numPoints = ps.length;
    const centroid = [x / numPoints, y / numPoints];
    const [cx, cy] = centroid;
    return (
      <View style={styles.container}>
        <Svg height={700} width={375} viewBox="0 0 100 100">
          <Polygon
            points={points}
            fill="lime"
            stroke="purple"
            strokeWidth="1"
          />
          <Circle cx={cx} cy={cy} r={1} />
        </Svg>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});
msand commented 5 years ago

Or like this?

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Svg, Polygon, Circle, G } from 'react-native-svg';
import Constants from 'expo-constants';

const centeredPolygon = points => {
  const ps = points.split(' ').map(p => p.split(',').map(Number));
  const sum = ps.reduce(([x0, y0], [x1, y1]) => [x0 + x1, y0 + y1]);
  const [x, y] = sum;
  const numPoints = ps.length;
  const centroid = [x / numPoints, y / numPoints];
  const [cx, cy] = centroid;
  return (
    <G transform={`translate(${-cx}, ${-cy})`}>
      <Polygon points={points} fill="lime" stroke="purple" strokeWidth="1" />
    </G>
  );
};

export default class App extends React.Component {
  render() {
    const polygon1 = centeredPolygon('40,5 70,80 25,95');
    const polygon2 = centeredPolygon('10,35 60,98 5,99');
    return (
      <View style={styles.container}>
        <Svg height={700} width={375} viewBox="0 0 100 100">
          <G transform="translate(50, 50)">
            {polygon1}
            {polygon2}
            <Circle cx={0} cy={0} r={1} />
          </G>
        </Svg>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});
Atuldhaka commented 5 years ago

You mean like this? https://snack.expo.io/HJHxWLe3B https://en.wikipedia.org/wiki/Centroid#Balancing_method

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { Svg, Polygon, Circle } from 'react-native-svg';
import Constants from 'expo-constants';

export default class App extends React.Component {
  render() {
    const points = '40,5 70,80 25,95';
    const ps = points.split(' ').map(p => p.split(',').map(Number));
    const sum = ps.reduce(([x0, y0], [x1, y1]) => [x0 + x1, y0 + y1]);
    const [x, y] = sum;
    const numPoints = ps.length;
    const centroid = [x / numPoints, y / numPoints];
    const [cx, cy] = centroid;
    return (
      <View style={styles.container}>
        <Svg height={700} width={375} viewBox="0 0 100 100">
          <Polygon
            points={points}
            fill="lime"
            stroke="purple"
            strokeWidth="1"
          />
          <Circle cx={cx} cy={cy} r={1} />
        </Svg>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
});

Hi i just need to put the image on the centroid of the polygon like marker for the map, i am attaching a sample image of what i am trying to do. Screen Shot 2019-11-19 at 11 21 55 AM

msand commented 4 years ago

Then calculate the centroid and place the marker there, what's the issue?

Atuldhaka commented 4 years ago

Hi @msand thanks for the help you saved me a lot time, i am done with putting the image in centroid. can you please let me know that how can i zoom the Svg. I need to zoom my map.

msand commented 4 years ago

https://www.npmjs.com/package/zoomable-svg or one of these https://github.com/react-native-community/react-native-svg/issues/1064

Atuldhaka commented 4 years ago

https://github.com/react-native-community/react-native-svg/issues/1064#issuecomment-518225872 hi i am using this for zooming but when i zoom, the svg do not give touch events how do i manage this please suggest some solution?

msand commented 4 years ago

At least in InfiniDraw I toggle between drawing and zooming modes: https://github.com/msand/InfiniDraw/blob/master/lib/drawing.js#L321-L325

You can probably achieve something similar with react-native-gesture-handler & reanimated, perhaps try asking on stackoverflow or the gesture-handler repo

Atuldhaka commented 4 years ago

Hi @msand, i m facing the issue that when i am loading the image with the help of uri on the svg then sometimes my app crashes and this occurs mostly when the internet is weak please help me with this. here is my sample code : -

`import React, { Component, Fragment } from "react"; import { Animated, StyleSheet, View, TouchableOpacity } from "react-native";

import { PanGestureHandler, PinchGestureHandler, State } from "react-native-gesture-handler"; import constants from "../../../constants"; import Svg, { Polygon, Image, Text } from "react-native-svg"; import { buttonType } from "../../../constants/Interfaces"; import Store from "../../../Store"; import { observer } from "mobx-react"; import constStrings from "../../../constants/constStrings"; import { isNullUndefined } from "../../../constants/Utils"; interface Props { buttonType: number; navigationProp: any; toOpenPopup: Function; arrayToDrawMap: any; }

@observer class SampleSvg extends Component { state = { myInitialAnimation: new Animated.ValueXY({ x: 0, y: 0 }) };

panRef = React.createRef(); pinchRef = React.createRef();

/ Pinching / _baseScale = new Animated.Value(1); _pinchScale = new Animated.Value(1); _scale = Animated.multiply(this._baseScale, this._pinchScale); _lastScale = 1; _onPinchGestureEvent = Animated.event( [{ nativeEvent: { scale: this._pinchScale } }], { useNativeDriver: false } );

/ dragging / _translateX = new Animated.Value(0); _translateY = new Animated.Value(0); _lastOffset = { x: 0, y: 0 }; _onGestureEvent = Animated.event( [ { nativeEvent: { translationX: this._translateX, translationY: this._translateY } } ], { useNativeDriver: false } );

_onPinchHandlerStateChange = (event: any) => { console.log("event", event); console.log(" this._lastScale", this._lastScale); console.log("this._baseScale", this._baseScale); console.log("State", State);

if (event.nativeEvent.oldState === State.ACTIVE) {
  this._lastScale *= event.nativeEvent.scale;
  if (this._lastScale < 0.8) {
    this._lastScale = 0.8;
  }
  this._baseScale.setValue(this._lastScale);
  this._pinchScale.setValue(1);
}

};

_onHandlerStateChange = event => { if (event.nativeEvent.oldState === State.ACTIVE) { this._lastOffset.x += event.nativeEvent.translationX; this._lastOffset.y += event.nativeEvent.translationY; this._translateX.setOffset(this._lastOffset.x); this._translateX.setValue(0); this._translateY.setOffset(this._lastOffset.y); this._translateY.setValue(0); } };

_onHandlerButtonStateChange = () => { console.warn("called"); //this._translateX.setOffset(200); this._translateX.setValue(200); //this._translateY.setOffset(150); this._translateY.setValue(150); };

dummy = () => { // if (event.nativeEvent.oldState === State.ACTIVE) { console.log("this._lastOffset.x", this._lastOffset.x);

this._lastOffset.x += 100;
this._lastOffset.y += 50;
this._translateX.setOffset(this._lastOffset.x);
this._translateX.setValue(0);
this._translateY.setOffset(this._lastOffset.y);
this._translateY.setValue(0);
// }

};

// componentDidMount() { // setTimeout(() => this.dummy(), 5000); // }

toChangeLocationOnTapOfMap = (item: any) => { Store.MapStore.updateUserZone( item.zone_id, this.props.navigationProp, item ); };

_moveCurrentPosition = () => { console.warn("Called"); Animated.spring(this.state.myInitialAnimation, { toValue: { x: 100, y: 800 } }).start(); };

render() { var arrayForMap = this.props.arrayToDrawMap; let currentZoneDetail = JSON.parse( Store.UserStore.current.specificZoneDetail ); // console.log(this._translateX, this._translateY) return ( <PanGestureHandler ref={this.panRef} onGestureEvent={this._onGestureEvent} onHandlerStateChange={this._onHandlerStateChange} minDist={50} minPointers={1} maxPointers={1} avgTouches

<PinchGestureHandler ref={this.pinchRef} onGestureEvent={this._onPinchGestureEvent} onHandlerStateChange={this._onPinchHandlerStateChange}

<Animated.View style={[ { width: 1000, height: 900, //backgroundColor: 'lightblue', transform: [ { perspective: 200 }, { scale: this._scale }, { translateX: this._translateX }, { translateY: this._translateY } ] } ]}

{/* <TouchableOpacity onPress={this._onHandlerButtonStateChange} style={styles.fabStyle}

Press

</Animated.View> */}

        <Svg
          height={constants.vh(700)}
          width={constants.vw(1000)}
          background={"red"}
          //width="100%"
          // viewBox="0 0 100 100"
          //  originX = {300}
          //  originY = {100}
        >
          {arrayForMap.map((item: any) => {
            const points = item.coordinates;
            const ps = points
              .split(" ")
              .map((p: any) => p.split(",").map(Number));
            //@ts-ignore
            const sum = ps.reduce(([x0, y0], [x1, y1]) => [
              x0 + x1,
              y0 + y1
            ]);
            const [x, y] = sum;
            const numPoints = ps.length;
            const centroid = [x / numPoints, y / numPoints];
            const [cx, cy] = centroid;
            console.log("item", item.zone_id);

            return (
              <Fragment key={item.zone_id}>
                <Polygon
                  points={item.coordinates}
                  fill={constants.Colors.color_rgb249249249}
                  stroke={constants.Colors.color_170170170}
                  strokeWidth="1"
                  onPress={() => this.toChangeLocationOnTapOfMap(item)}
                />
                <Text
                  x={cx}
                  y={cy - 25}
                  textAnchor="middle"
                  fontWeight="bold"
                  fontSize={constants.vh(14)}
                  fill={constants.Colors.color_434343}
                  onPress={() => this.toChangeLocationOnTapOfMap(item)}
                  fontFamily={constants.fonts.QUICKSAND_BOLD}
                >
                  {item.zone_name}
                </Text>

                <Text
                  x={cx}
                  y={cy - 10}
                  textAnchor="middle"
                  fontWeight="bold"
                  fontSize={constants.vh(14)}
                  fill={constants.Colors.color_434343}
                  onPress={() => this.toChangeLocationOnTapOfMap(item)}
                  fontFamily={constants.fonts.QUICKSAND_BOLD}
                >
                  {isNullUndefined(item.temperature_c)
                    ? "-" +
                      `${
                        Store.UserStore.current.currentTempUnits ==
                        constants.AppConstant.temp_units.celsius
                          ? constStrings.degreeCelsius
                          : constStrings.degreefarneheit
                      }`
                    : +item.temperature_c.toFixed(0) +
                      `${
                        Store.UserStore.current.currentTempUnits ==
                        constants.AppConstant.temp_units.celsius
                          ? constStrings.degreeCelsius
                          : constStrings.degreefarneheit
                      }`}
                  {/* constStrings.degreeCelsius} */}
                </Text>

                <Image
                  x={cx - 10}
                  y={cy - 5}
                  width={
                    item.zone_id == Store.MapStore.selectedItem.zone_id
                      ? 32
                      : 21
                  }
                  height={
                    item.zone_id == Store.MapStore.selectedItem.zone_id
                      ? 38
                      : 25
                  }
                  preserveAspectRatio="xMidYMid slice"
                  opacity="0.5"
                  href={
                    item.zone_name === currentZoneDetail.zone_name
                      ? {
                          uri: constants.AppConstant.imageUrl.currentZone
                        }
                      : this.props.buttonType === buttonType.warmer &&
                        item.temperature_c >=
                          Store.UserStore.current.currentZoneTemperature
                      ? // && item.condition_type === this.props.buttonType
                        {
                          uri: constants.AppConstant.imageUrl.warmZone
                        }
                      : this.props.buttonType === buttonType.cooler &&
                        item.temperature_c <=
                          Store.UserStore.current.currentZoneTemperature
                      ? // &&  item.condition_type === this.props.buttonType
                        {
                          uri: constants.AppConstant.imageUrl.coolerZone
                        }
                      : this.props.buttonType === buttonType.free &&
                        (isNullUndefined(item.people_occupied)
                          ? {
                              uri: constants.AppConstant.imageUrl.freeZone
                            }
                          : item.people_occupied == false)
                      ? //  && item.condition_type === this.props.buttonType
                        {
                          uri: constants.AppConstant.imageUrl.freeZone
                        }
                      : {
                          uri: constants.AppConstant.imageUrl.greyZone
                        }
                  }
                  // onPress={() => this.toChangeLocationOnTapOfMap(item)}
                  onPress={() => this.props.toOpenPopup(item)}
                  // clipPath="url(#clip)"
                />
              </Fragment>
            );
          })}
        </Svg>
      </Animated.View>
    </PinchGestureHandler>
  </PanGestureHandler>
);

} }

export default SampleSvg; `