Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mirror and resize the part of image with fixed width and height

enter image description here

I'm building "Tagging from photo" functionality.

  1. When the user move or pinch the square on the image,
  2. PanResponder changes the state of x-coordinate(left), y-coordinate(top), the length of square(thumbSize)
  3. With the data, I want to show the part of square real-time

So this image below should be placed into the left of A, All All from the image above.

enter image description here

Here is the part of render showing the "cropped" image.

console.log(left) // 80
console.log(top) // 200
console.log(thumbSize) // 150
<Image
      source={{uri: image}}
      style={{height:70, width: 70, bottom: (-top), right: (-left)
      }} <- style is not complete. I'm putting some example code
/>

This is continuous problem from: How to show the only part of the image.

It works but the solution doesn't meet my expectation.

  • It's not changing width and height ( I want to fix resize the image from 'the width of square' to '70' for each width and height)
  • It breaks the whole style (A, All, All things disappear)

I've been trying to solve this idea for days but couldn't find the exact way.


Update: I almost solved it but resizing matters

I changed Image to CroppedImage (new component)

<CroppedImage
      source={{uri: image}}
      cropTop={top}
      cropLeft={left}
      cropWidth={thumbSize}
      cropHeight={thumbSize}
      width={width(100)}
      height={width(100)}
      resizeMode="contain" />

Here is CroppedImage

return (
  <View style={[{
    overflow: 'hidden',
    height: this.props.cropHeight,
    width: this.props.cropWidth,
    backgroundColor: 'transparent'
    }, this.props.style]}>
    <Image style={{
      position: 'absolute',
      top: this.props.cropTop * -1,
      left: this.props.cropLeft * -1,
      width: this.props.width,
      height: this.props.height
    }}
      source={this.props.source}
      resizeMode={this.props.resizeMode}>
      {this.props.children}
    </Image>
  </View>
);

It seems working but it can't resize (from square width x height to 70x70).

enter image description here

like image 308
merry-go-round Avatar asked Nov 22 '17 12:11

merry-go-round


1 Answers

Well, I finally managed to create a working React Native code (never used it before, sorry if it is noobish code) doing the same as in my other answer.

Here is the code:

import React, { Component } from 'react';
import { TouchableWithoutFeedback, ImageBackground, Image, View, StyleSheet } from 'react-native';

const IMAGEURI = 'http://fakeimg.pl/300x300/';
const SIZERATIO = .6;
const IMAGEWIDTH = 300;
const IMAGEHEIGHT = 300;
const CROPIMAGEWIDTH = 100;
const CROPIMAGEHEIGHT = 100;

export default class App extends Component {
  state = {
    style: {
      marginLeft: 0,
      marginTop: 0,
    },
    uri: ''
  };

  repositionImage(event) {
    this.setState({
      style: {
        marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
        marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
      },
      uri: IMAGEURI
    });
  }

  render() {
    return (
      <View>
        <TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>
          <View>
            <Image
              style={styles.image}
              source={{ uri: IMAGEURI }}
            />
          </View>
        </TouchableWithoutFeedback>
        <View style={styles.tag}>
          <ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  image: {
    width: IMAGEWIDTH,
    height: IMAGEHEIGHT,
  },

  tag: {
    borderWidth: 1,
    borderColor: '#000',
    width: CROPIMAGEWIDTH,
    height: CROPIMAGEHEIGHT,
    overflow: 'hidden'
  },

  cropped: {
    width: IMAGEWIDTH*SIZERATIO,
    height: IMAGEHEIGHT*SIZERATIO
  }
});

And here is the Snack

I really hope it helped!! Good luck!!

EDIT: Ok I will explain a bit what I'm doing here.

First, I set a State with the parameters that will change based on some event:

 state = {
    style: {
      marginLeft: 0,
      marginTop: 0,
    },
    uri: ''
  };

Then, I make the component to get its properties from that state:

< ImageBackground style={[styles.cropped,this.state.style]} source={{uri: this.state.uri }} />

Finally, I prepare the onPress event to call a function which will update the state:

< TouchableWithoutFeedback onPress={(event) => this.repositionImage(event)}>

Here I'm feeding my function with the event object so I will be available to get the coordinates where the user pressed.

That last function takes the data from the event and updates the state. The view will automatically refresh with the new state data.

repositionImage(event) {
  this.setState({
    style: {
      marginLeft: CROPIMAGEWIDTH/2 - event.nativeEvent.locationX*SIZERATIO,
      marginTop: CROPIMAGEHEIGHT/2 - event.nativeEvent.locationY*SIZERATIO
    },
    uri: IMAGEURI
  });
}

To position the image, I simply do a math operation:

CROPIMAGEWIDTH is the width of my tag element so to get the center I divide it by 2. Then, I substract the locationX of the event to move the image to the left so the locationX will be at the center of the tag.

That is only for positioning. To scale it just multiply the size of the image and the locationX by the same value. Note I multiplied the width and height of the image with the SIZERATIO in the cropped style

cropped: {
  width: IMAGEWIDTH*SIZERATIO,
  height: IMAGEHEIGHT*SIZERATIO
}

An example of this scaling stuff:

If your image has 200 width and you want to scale it to a half, you multiply it by 0.5. So if you click at the, say, pixel 180 starting for the left, the equivalent pixel for your scaled image will have to be multiplied by 0.5 too and it will be 90.

If there is something I didn't explaing clearly enough just ask me again a I will be glad to help you.

like image 96
FcoRodr Avatar answered Sep 20 '22 12:09

FcoRodr