How I can get Image coordinate in Nativescript with Angular2 - nativescript

I have an Image to show in my nativescript(with Angular2) app, where I want to make different part of image clickable. For example a human body image and I just want to know which part is clicked by the user.
Is there any way to create image-map just like html???
<CardView height="450" width="350" marginTop="10">
<Image src="res://nerves" height="304" width="114" horizontalAlignment="center" verticalAlignment="center"></Image>
</CardView>

Use the (touch) event binding on your Image element.
Here's an example that prints a console message when you click in the fourth quadrant of the image.
import {
Component
} from '#angular/core';
import * as platform from 'platform';
import {
TouchGestureEventData
} from 'tns-core-modules/ui/gestures';
#Component({
moduleId: module.id,
selector: 'your-component',
template: `
<GridLayout>
<Image src="res://your_image" width="128" height="128"
(touch)="touchImage($event)"
verticalAlignment="middle" horizontalAlignment="center"></Image>
</GridLayout>
`
})
export class YourComponent {
touchImage(event: TouchGestureEventData) {
// This is the density of your screen, so we can divide the measured width/height by it.
const scale: number = platform.screen.mainScreen.scale;
if (event.action === 'down') {
// this is the point that the user just clicked on, expressed as x/y
// values between 0 and 1.
const point = {
y: event.getY() / (event.view.getMeasuredHeight() / scale),
x: event.getX() / (event.view.getMeasuredWidth() / scale)
};
// add custom code to figure out if something significant was "hit"
if (point.x > 0.5 && point.y > 0.5) {
console.log('you clicked on the lower right part of the image.');
}
}
}
}

Related

FAB Menu Nativescript with Angular

I'm building an app with angular and nativescript , and i want a fab button like this one fab menu vuejs
Does anyone have example or snippet for angular ?
I am not very good with css and i don't know how to something like on angular.
Note: I'm also very new to Nativescript/Angular. I might miss some details, feel free to edit this answer to correct me.
I used this so I would not have to make the FAB myself: https://market.nativescript.org/plugins/nativescript-floatingactionbutton. You can add it to your project by running tns plugin add nativescript-floatingactionbutton.
I don't feel like the documentation is very clear.. I went through those links to come up with something:
https://github.com/nstudio/nativescript-floatingactionbutton/issues/95 (your question basically)
https://github.com/jlooper/nativescript-snacks/blob/master/_posts/2016-06-05-fab-nav-angular.markdown (linked in the last answer of the previous link.. outdated.. removed from the doc)
https://docs.nativescript.org/angular/ui/animation
First, the layout of my page is a GridLayout. I feel like it won't work otherwise. I was testing with a StackLayout first.. no luck.
Inside this GridLayout, I have other stuff (in my case a ListView) and I added at the end another GridLayout.
<GridLayout rows="auto, *">
...
<GridLayout row="1", rows="auto, *">
<Fab row="1" #btna icon="res://first_option_icon" rippleColor="#f1f1f1" class="fab-button btna"></Fab>
<Fab row="1" #btnb icon="res://second_option_icon" rippleColor="#f1f1f1" class="fab-button btnb"></Fab>
<Fab row="1" #fab (tap)="displayOptions()" icon="res://add_icon" rippleColor="#f1f1f1" class="fab-button"></Fab>
</GridLayout>
</GridLayout>
In the exemple from github, buttons are used instead of fab for the "children". The only reason I replaced it with a fab here is because I download my icons from https://material.io/resources/icons and buttons don't accept icons (when downloading an icon from material.io, you can choose "android" (or iOS) in the download options, it gives different sizes of the icon).
Using fab instead of buttons, the css becomes a little easier as well (unless you want to make them smaller).
.fab-button {
height: 70;
/*width: 70; -- Needed for iOS only*/
margin: 15;
background-color: orangered;
horizontal-align: right;
vertical-align: bottom;
}
.btna {
background-color: #493DF8;
}
.btnb {
background-color: #1598F6;
}
And then all that's left is the javascript.
// Necessary imports
import { ..., ViewChild, ElementRef } from "#angular/core";
import { registerElement } from "#nativescript/angular/element-registry";
import { Fab } from "nativescript-floatingactionbutton";
import { View } from "tns-core-modules";
registerElement("Fab", () => Fab);
#Component(...)
export class YourComponent {
...
// Reference those fabs
public _isFabOpen: Boolean;
#ViewChild("btna") btna: ElementRef;
#ViewChild("btnb") btnb: ElementRef;
#ViewChild("fab") fab: ElementRef;
...
displayOptions() {
if (this._isFabOpen) {
// Rotate main fab
const fab = <View>this.fab.nativeElement;
fab.animate({rotate: 0, duration: 280, delay: 0});
// Show option 1
const btna = <View>this.btna.nativeElement;
btna.animate({translate: { x: 0, y: 0 }, opacity: 0, duration: 280, delay: 0});
// Show option 2
const btnb = <View>this.btnb.nativeElement;
btnb.animate({translate: { x: 0, y: 0 }, opacity: 0, duration: 280, delay: 0});
this._isFabOpen = false;
} else {
// Rotate main fab
const view = <View>this.fab.nativeElement;
view.animate({rotate: 45, duration: 280, delay: 0});
// Show option 1
const btna = <View>this.btna.nativeElement;
btna.animate({translate: { x: 0, y: -80 }, opacity: 1, duration: 280, delay: 0});
// Show option 2
const btnb = <View>this.btnb.nativeElement;
btnb.animate({translate: { x: 0, y: -145 }, opacity: 1, duration: 280, delay: 0});
this._isFabOpen = true;
}
}
}
Tada!

Nativescript-Vue Issue with Panning Elements around

So I am building a photo group creator in Nativescript-vue. I have the pan working and the view is rendering correctly. But my issue is when a user starts to pan the image outside the container it's in, the image goes 'behind' the container. And I need it to render ontop of everything. I have tried 'z-index' with css and still no go.
The idea is, there is a group of photos (un-grouped group). And the user will be able to click-and-drag a photo in the group down to an 'empty' group. And this in turn would create a new group. Or if there were other groups the user would be able to drag an image and add it to an existing group.
Any help or suggestions, thank you in advance!
This is my Vue Component
<template>
<StackLayout orientation="vertical" ref="mainContainer">
</StackLayout>
</template>
<script>
import * as StackLayout from 'tns-core-modules/ui/layouts/stack-layout';
import * as Image from 'tns-core-modules/ui/image';
import _ from 'underscore';
export default {
name: "photo-groupings",
props:{
photoList:{
type: Object,
required: true
}
},
mounted(){
let ungrouped = this.createGroupElement();
let newGroupElement = this.createGroupElement();
console.log(_.keys(this.photoList));
for(let pht of _.keys(this.photoList)){
console.log(pht);
console.log(this.photoList[pht]);
ungrouped.addChild(this.createImageChild(this.photoList[pht]))
}
this.$refs.mainContainer.nativeView.addChild(ungrouped);
this.$refs.mainContainer.nativeView.addChild(newGroupElement)
},
data(){
return {
photoGroups:{},
groupElements:{},
prevDeltaX: 0,
prevDeltaY: 0
}
},
methods:{
createImageChild(src){
let tempImg = new Image.Image();
tempImg.src = src;
tempImg.width = 100;
tempImg.height = 100;
tempImg.stretch = "aspectFill";
tempImg.borderRadius = 10;
tempImg.borderWidth = 2;
tempImg.borderColor = "forestgreen";
tempImg.margin = 5;
tempImg.on('pan', this.handlePan);
return tempImg;
},
createGroupElement(){
let tempGroup = new StackLayout.StackLayout();
tempGroup.orientation = "horizontal";
tempGroup.margin = 5;
tempGroup.borderColor = "black";
tempGroup.borderRadius = 5;
tempGroup.borderWidth = 1;
tempGroup.minHeight = 110;
return tempGroup;
},
handlePan(args){
if (args.state === 1) // down
{
this.prevDeltaX = 0;
this.prevDeltaY = 0;
console.log(args.view.getLocationRelativeTo(args.view.parent));
console.log(args.view.parent.getLocationInWindow());
}
else if (args.state === 2) // panning
{
args.view.translateX += args.deltaX - this.prevDeltaX;
args.view.translateY += args.deltaY - this.prevDeltaY;
this.prevDeltaX = args.deltaX;
this.prevDeltaY = args.deltaY;
}
else if (args.state === 3) // up
{
console.log("Pan release")
}
}
}
}
</script>
<style scoped>
</style>
Example Image of the images rendering 'behind'
The best way here would be creating a ghost element.
You should not move the original image but hide the original image when you detect drag, create a ghost of original image and insert it on the parent layout. When user drops the ghost image, destroy the ghost and move original image to destination.

How to create events using React Native

I'm making an application using React VR. If you don't know React VR, well it's based on React Native with some other components, includes Three.js and other stuff, specific for using WebVR.
I've making a component named NavigateButton. Below is my code:
import React from 'react';
import { AppRegistry, asset, StyleSheet, Pano, Text, View, VrButton, Sphere } from 'react-vr';
export class NavigateButton extends React.Component {
render() {
return (
<VrButton onClick={() => this.onNavigating()}>
<Sphere radius={0.5} widthSegments={10} heightSegments={10} style={{ color: "red" }} />
</VrButton>
);
}
onNavigating() { // This method must throw an event
console.log(this.props.to);
}
};
If the user clicks on the VrButton (this is like a HTML 5 button-tag but for VR with inside it, a sphere), an event must been raised to the place where I call the NavigateButton component. That's on code below:
import React from 'react';
import { AppRegistry, asset, StyleSheet, Pano, Text, View, VrButton, Sphere } from 'react-vr';
import { NavigateButton } from './components/nativateButton.js';
let room = asset('360 LR/inkom_hal.jpg');
export default class MainComp extends React.Component {
render() {
return (
<View>
<Pano source={asset('360 LR/inkom_hal.jpg')} />
<View style={{ transform: [{ translate: [20, 0, 0] }] }}>
<NavigateButton to="garage"></NavigateButton>
<!-- and must been catch here -->
</View>
<View style={{ transform: [{ translate: [-7, 0, -20] }] }}>
<NavigateButton to="woonkamer"></NavigateButton>
<!-- or here -->
</View>
</View>
);
}
}
AppRegistry.registerComponent('MainComp', () => MainComp);
Is it possible to do that? I would something like code below to catch the event:
<NavigateButton to="woonkamer" onNavigate={() => this.change()}></NavigateButton>
I've searched on the internet but nothing found that could help me.
Here is the instruction how to create Sample VR app with React VR prepared by me and my team:
Creating a VR tour for web
The structure of future app’s directory is as follows:
+-node_modules
+-static_assets
+-vr
\-.gitignore
\-.watchmanconfig
\-index.vr.js
\-package.json
\-postinstall.js
\-rn-cli-config.js
The code of a web app would be in the index.vr.js file, while the static_assets directory hosts external resources (images, 3D models). You can learn more on how to get started with React VR project here. The index.vr.js file contains the following:
import React from 'react';
import {
AppRegistry,
asset,
StyleSheet,
Pano,
Text,
View,
}
from 'react-vr';
class TMExample extends React.Component {
render() {
return (
<View>
<Pano source={asset('chess-world.jpg')}/>
<Text
style={{
backgroundColor:'blue',
padding: 0.02,
textAlign:'center',
textAlignVertical:'center',
fontSize: 0.8,
layoutOrigin: [0.5, 0.5],
transform: [{translate: [0, 0, -3]}],
}}>
hello
</Text>
</View>
);
}
};
AppRegistry.registerComponent('TMExample', () => TMExample);
VR components in use
We use React Native packager for code pre-processing, compilation, bundling and asset loading. In render function there are view, pano and text components. Each of these React VR components comes with a style attribute to help control the layout.
To wrap it up, check that the root component gets registered with AppRegistry.registerComponent, which bundles the application and readies it to run. Next step to highlight in our React VR project is compiling 2 main files.
Index.vr.js file
In constructor we’ve indicated the data for VR tour app. These are scene images, buttons to switch between scenes with X-Y-Z coordinates, values for animations. All the images we contain in static_assets folder.
constructor (props) {
super(props);
this.state = {
scenes: [{scene_image: 'initial.jpg', step: 1, navigations: [{step:2, translate: [0.73,-0.15,0.66], rotation: [0,36,0] }] },
{scene_image: 'step1.jpg', step: 2, navigations: [{step:3, translate: [-0.43,-0.01,0.9], rotation: [0,140,0] }]},
{scene_image: 'step2.jpg', step: 3, navigations: [{step:4, translate: [-0.4,0.05,-0.9], rotation: [0,0,0] }]},
{scene_image: 'step3.jpg', step: 4, navigations: [{step:5, translate: [-0.55,-0.03,-0.8], rotation: [0,32,0] }]},
{scene_image: 'step4.jpg', step: 5, navigations: [{step:1, translate: [0.2,-0.03,-1], rotation: [0,20,0] }]}],
current_scene:{},
animationWidth: 0.05,
animationRadius: 50
};
}
Then we’ve changed the output of images linking them to state, previously indicated in constructor.
<View>
<Pano source={asset(this.state.current_scene['scene_image'])}
style={{
transform: [{translate: [0, 0, 0]}]
}}/>
</View>
Navigational buttons
In each scene we’ve placed transition buttons for navigation within a tour, taking data from state. Subscribing to onInput event to convey switching between scenes, binding this to it as well.
<View>
<Pano source={asset(this.state.current_scene['scene_image'])} onInput={this.onPanoInput.bind(this)}
onLoad={this.sceneOnLoad} onLoadEnd={this.sceneOnLoadEnd}
style={{ transform: [{translate: [0, 0, 0]}] }}/>
{this.state.current_scene['navigations'].map(function(item,i){
return <Mesh key={i}
style={{
layoutOrigin: [0.5, 0.5],
transform: [{translate: item['translate']},
{rotateX: item['rotation'][0]},
{rotateY: item['rotation'][1]},
{rotateZ: item['rotation'][2]}]
}}
onInput={ e => that.onNavigationClick(item,e)}>
<VrButton
style={{ width: 0.15,
height:0.15,
borderRadius: 50,
justifyContent: 'center',
alignItems: 'center',
borderStyle: 'solid',
borderColor: '#FFFFFF80',
borderWidth: 0.01
}}>
<VrButton
style={{ width: that.state.animationWidth,
height:that.state.animationWidth,
borderRadius: that.state.animationRadius,
backgroundColor: '#FFFFFFD9'
}}>
</VrButton>
</VrButton>
</Mesh>
})}
</View>
onNavigationClick(item,e){
if(e.nativeEvent.inputEvent.eventType === "mousedown" && e.nativeEvent.inputEvent.button === 0){
var new_scene = this.state.scenes.find(i => i['step'] === item.step);
this.setState({current_scene: new_scene});
postMessage({ type: "sceneChanged"})
}
}
sceneOnLoad(){
postMessage({ type: "sceneLoadStart"})
}
sceneOnLoadEnd(){
postMessage({ type: "sceneLoadEnd"})
}
this.sceneOnLoad = this.sceneOnLoad.bind(this);
this.sceneOnLoadEnd = this.sceneOnLoadEnd.bind(this);
this.onNavigationClick = this.onNavigationClick.bind(this);
Button animation
Below, we’ll display the code for navigation button animations. We’ve built animations on button increase principle, applying conventional requestAnimationFrame.
this.animatePointer = this.animatePointer.bind(this);
animatePointer(){
var delta = this.state.animationWidth + 0.002;
var radius = this.state.animationRadius + 10;
if(delta >= 0.13){
delta = 0.05;
radius = 50;
}
this.setState({animationWidth: delta, animationRadius: radius})
this.frameHandle = requestAnimationFrame(this.animatePointer);
}
componentDidMount(){
this.animatePointer();
}
componentWillUnmount(){
if (this.frameHandle) {
cancelAnimationFrame(this.frameHandle);
this.frameHandle = null;
}
}
In componentWillMount function we’ve indicated the current scene. Then we’ve also subscribed to message event for data exchange with the main thread. We do it this way due to a need to work out a React VR component in a separate thread.
In onMainWindowMessage function we only process one message with newCoordinates key. We’ll elaborate later why we do so. Similarly, we’ve subscribed to onInput event to convey arrow turns.
componentWillMount(){
window.addEventListener('message', this.onMainWindowMessage);
this.setState({current_scene: this.state.scenes[0]})
}
onMainWindowMessage(e){
switch (e.data.type) {
case 'newCoordinates':
var scene_navigation = this.state.current_scene.navigations[0];
this.state.current_scene.navigations[0]['translate'] = [e.data.coordinates.x,e.data.coordinates.y,e.data.coordinates.z]
this.forceUpdate();
break;
default:
return;
}
}
<Pano source={asset(this.state.current_scene['scene_image'])} onInput={this.onPanoInput.bind(this)}
style={{ transform: [{translate: [0, 0, 0]}] }}/>
rotatePointer(nativeEvent){
switch (nativeEvent.keyCode) {
case 38:
this.state.current_scene.navigations[0]['rotation'][1] += 4;
break;
case 39:
this.state.current_scene.navigations[0]['rotation'][0] += 4;
break;
case 40:
this.state.current_scene.navigations[0]['rotation'][2] += 4;
break;
default:
return;
}
this.forceUpdate();
}
Arrow turns are done with ↑→↓ alt keys, for Y-X-Z axes respectively.
See and download the whole index.vr.js file on Github HERE.
Client.js file
Moving further into our React VR example of virtual reality web applications, we’ve added the code below into init function. The goal is processing of ondblclick, onmousewheel and message events, where the latter is in rendering thread for message exchanges. Also, we’ve kept a link to vr and vr.player._camera objects.
window.playerCamera = vr.player._camera;
window.vr = vr;
window.ondblclick= onRendererDoubleClick;
window.onmousewheel = onRendererMouseWheel;
vr.rootView.context.worker.addEventListener('message', onVRMessage);
We’ve introduced the onVRMessage function for zoom returning to default when scenes change. Also, we have added the loader when scene change occurs.
function onVRMessage(e) {
switch (e.data.type) {
case 'sceneChanged':
if (window.playerCamera.zoom != 1) {
window.playerCamera.zoom = 1;
window.playerCamera.updateProjectionMatrix();
}
break;
case 'sceneLoadStart':
document.getElementById('loader').style.display = 'block';
break;
case 'sceneLoadEnd':
document.getElementById('loader').style.display = 'none';
break;
default:
return;
}
}
onRendererDoubleClick function for 3D-coordinates calculation and sending messages to vr component to change arrow coordinates. The get3DPoint function is custom to our web VR application and looks like this:
function onRendererDoubleClick(){
var x = 2 * (event.x / window.innerWidth) - 1;
var y = 1 - 2 * ( event.y / window.innerHeight );
var coordinates = get3DPoint(window.playerCamera, x, y);
vr.rootView.context.worker.postMessage({ type: "newCoordinates", coordinates: coordinates });
}
Switch to mouse wheel
We’ve used the onRendererMouseWheel function for switching zoom to a mouse wheel.
function onRendererMouseWheel(){
if (event.deltaY > 0 ){
if(window.playerCamera.zoom > 1) {
window.playerCamera.zoom -= 0.1;
window.playerCamera.updateProjectionMatrix();
}
}
else {
if(window.playerCamera.zoom < 3) {
window.playerCamera.zoom += 0.1;
window.playerCamera.updateProjectionMatrix();
}
}
}
Exporting coordinates
Then we’ve utilized Three.js to work with 3D-graphics. In this file we’ve only conveyed one function to export screen coordinated to world coordinates.
import * as THREE from 'three';
export function get3DPoint(camera,x,y){
var mousePosition = new THREE.Vector3(x, y, 0.5);
mousePosition.unproject(camera);
var dir = mousePosition.sub(camera.position).normalize();
return dir;
}
See and download the whole client.js file on Github HERE. There’s probably no need to explain how the cameraHelper.js file works, as it is plain simple, and you can download it as well.
Also, if you are interested in a lookalike project estimate or same additional technical details about ReactVR development - you can find some info here:

Move UI element with keyboard nativescript (IOS)

On my nativescript app - I have a button at the bottom of a screen. On the screen there is a Text area. When the user taps in the Text Area, a virtual keyboard appears. At this point, I want the button at the bottom to move up and appear just on top of the virtual keyboard. Any suggestions on how I can achieve this in both android and iOS?
Code
<GridLayout>
<ActionBar title="" backgroundColor="#f82462" top="0" left="0">
<NavigationButton (tap)="goBack()"></NavigationButton>
</ActionBar>
<GridLayout rows="*, auto">
<GridLayout row ='0' rows="auto *" columns="">
<GridLayout row="0" rows="" columns="">
<Button text="Top Button" (tap)="goNext()"></Button>
</GridLayout>
<GridLayout row="1" backgroundColor="#f82462">
<TextView [(ngModel)]="xyz" class="input" hint="Write your question as a complete sentence.Click on camera to add images if required." returnkeyType="done" id="questionText"></TextView>
</GridLayout>
</GridLayout>
<StackLayout row='1'>
<Button text="Next" (tap)="goNext()"></Button>
</StackLayout>
</GridLayout>
I am not able to test this right now, but have you tried to wrap everything inside the main GridLayout in <ScrollView> ... </ScrollView>
I also encountered this problem for my instant chat application, here is the solution : https://gist.github.com/elvticc/0c789d08d57b1f4d9273f7d93a7083ec
// Also use IQKeyboardManager to customize the iOS keyboard
// See https://github.com/tjvantoll/nativescript-IQKeyboardManager
// let iqKeyboard: IQKeyboardManager = IQKeyboardManager.sharedManager();
// iqKeyboard.toolbarDoneBarButtonItemText = "OK";
// iqKeyboard.canAdjustAdditionalSafeAreaInsets = true;
// iqKeyboard.shouldFixInteractivePopGestureRecognizer = true;
// Angular
[...]
import { OnInit, OnDestroy, ElementRef, ViewChild } from "#angular/core";
[...]
// NativeScript
[...]
import { ios as iosApp } from "tns-core-modules/application/application";
[...]
#ViewChild("element") private _element: ElementRef<StackLayout>; // FlexboxLayout, GridLayout, etc.
private _keyboardHeight: number = 0;
private _elementHeight: number = 0;
private _observerIDs: Array<object> = new Array();
// Start events when the component is ready
ngOnInit(): void {
// iOS keyboard events
if (iosApp) {
let eventNames: Array<string> = [
UIKeyboardWillShowNotification,
UIKeyboardDidShowNotification,
UIKeyboardWillHideNotification
];
// Catch the keyboard height before it appears
this._observerIDs.push({
event: eventNames[0],
id: iosApp.addNotificationObserver(eventNames[0], (event) => {
let currHeight: number = this._keyboardHeight,
newHeight: number = event.userInfo.valueForKey(UIKeyboardFrameEndUserInfoKey).CGRectValue.size.height;
if (currHeight != newHeight) {
this._keyboardHeight = newHeight;
}
})
});
// Position the element according to the height of the keyboard
this._observerIDs.push({
event: eventNames[1],
id: iosApp.addNotificationObserver(eventNames[1], (event) => {
if (this._elementHeight == 0) {
this._elementHeight = this._element.nativeElement.getActualSize().height;
}
this._element.nativeElement.height = this._keyboardHeight + this._elementHeight;
})
});
// Reposition the element according to its starting height
this._observerIDs.push({
event: eventNames[2],
id: iosApp.addNotificationObserver(eventNames[2], () => {
this._element.nativeElement.height = this._elementHeight; // or "auto";
})
});
}
}
// Stop events to avoid a memory leak
ngOnDestroy(): void {
if (iosApp) {
let index: number = 0;
for (index; index < this._observerIDs.length; index++) {
let observerId: number = this._observerIDs[index]['id'],
eventName: string = this._observerIDs[index]['event'];
iosApp.removeNotificationObserver(observerId, eventName);
}
}
}
Marcel Ploch's original : https://gist.github.com/marcel-ploch/bf914dd62355049a0e5efb4885ca4c6e

Making SVG Responsive in React

I am working on a responsive utility component, to make a few D3 components responsive in react. However I deep SVG knowledge escapes me. I have based my responsive utility on this issue on github. However it isn't quite working, All it does is render the a chart, but not at the width or height passed in but rather at a really small width and height. It also doesn't resize.
import React from 'react';
class Responsive extends React.Component{
constructor () {
super();
this.state = {
size: {
w: 0,
h: 0
}
}
}
componentDidMount () {
window.addEventListener('resize', this.fitToParentSize.bind(this));
this.fitToParentSize();
}
componentWillReceiveProps () {
this.fitToParentSize();
}
componentWillUnmount() {
window.removeEventListener('resize', this.fitToParentSize.bind(this));
}
fitToParentSize () {
let elem = this.findDOMNode(this);
let w = elem.parentNode.offsetWidth;
let h = elem.parentNode.offsetHeight;
let currentSize = this.state.size;
if (w !== currentSize.w || h !== currentSize.h) {
this.setState({
size: {
w: w,
h: h
}
});
}
}
render () {
let {width, height} = this.props;
width = this.state.size.w || 100;
height = this.state.size.h || 100;
var Charts = React.cloneElement(this.props.children, { width, height});
return Charts;
}
};
export default Responsive;
Responsive width={400} height={500}>
<XYAxis data={data3Check}
xDataKey='x'
yDataKey='y'
grid={true}
gridLines={'solid'}>
<AreaChart dataKey='a'/>
<LineChart dataKey='l' pointColor="#ffc952" pointBorderColor='#34314c'/>
</XYAxis>
</Responsive>
disclaimer: I'm the author of vx a low-level react+d3 library full of visualization components.
You could use #vx/responsive or create your own higher-order component based on withParentSize() or withWindowSize() depending on what sizing you want to respond to (I've found most situations require withParentSize()).
The gist is you create a higher-order component that takes in your chart component and it attaches/removes event listeners for when the window resizes with a debounce time of 300ms by default (you can override this with a prop) and stores the dimensions in its state. The new parent dimensions will get passed in as props to your chart as parentWidth, parentHeight or screenWidth, screenHeight and you can set your svg's width and height attributes from there or calculate your chart dimensions based on those values.
Usage:
// MyChart.js
import { withParentSize } from '#vx/responsive';
function MyChart({ parentWidth, parentHeight }) {
return (
<svg width={parentWidth} height={parentHeight}>
{/* stuff */}
</svg>
);
}
export default withParentSize(MyChart);

Resources