Qian�Ao Rigele 2020-05-23 10:17 采纳率: 0%
浏览 21
已结题

react.js 如何正确设置Setstate,让两个图层:散点图层ScatterplotLayer和线条图层TripsLayer能够实现同步渲染。

(1)在deck.gl中实现两个图层叠加后的可视化效果。现在实现的效果线条图层TripsLayer因为添加了animate可以自动渲染,但是散点图层ScatterplotLayer只能通过拖动下方的时间轴进度条实现动画效果,如下图。也就是,在叠加之后,它们现在似乎是两个不相关的层。在ScatterplotLayer(添加了DataFilterExtension)中,圆半径仍由屏幕底部的进度条控制。拖动可更改大小,而不是类似于TripsLayer自动渲染。

想让它们根据共同属性时间戳timestamp实现同步渲染。也就是说,当TripsLayer的轨迹移动时,ScatterplotLayer的圆半径也会随之变化。之前也在ScatterplotLayer里面添加了animate,但是没有动画效果,可能是Setstate没有设置正确。非常感谢您的帮助~~

完整代码和现在实现的效果截图如下:

import React, {Component, Fragment} from 'react';

import {render} from 'react-dom';

import {StaticMap} from 'react-map-gl';

import {AmbientLight, PointLight, LightingEffect} from '@deck.gl/core';

import DeckGL from '@deck.gl/react';

import {PolygonLayer} from '@deck.gl/layers';

import {TripsLayer} from '@deck.gl/geo-layers';

import {ScatterplotLayer} from '@deck.gl/layers';

import {DataFilterExtension} from '@deck.gl/extensions';

import {MapView} from '@deck.gl/core';

import RangeInput from './range-input';


// Set your mapbox token here

const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line


// Source data CSV

const DATA_URL1 = {

 TRIPS:

   './package1.json' // eslint-disable-line

};

const DATA_URL =

 './data2.csv'; // eslint-disable-line


const MAP_VIEW = new MapView({

   // 1 is the distance between the camera and the ground

   farZMultiplier: 100

 });


const ambientLight = new AmbientLight({

 color: [122, 122, 122],

 intensity: 1.0

});


const pointLight = new PointLight({

 color: [255, 255, 255],

 intensity: 2.0,

 position: [127.05, 37.5, 8000]

});


const lightingEffect = new LightingEffect({ambientLight, pointLight});


const material = {

 ambient: 0.1,

 diffuse: 0.6,

 shininess: 32,

 specularColor: [60, 64, 70]

};


const DEFAULT_THEME = {

 buildingColor: [74, 80, 87],

 trailColor0: [253, 128, 93],

 trailColor1: [23, 184, 190],

 material,

 effects: [lightingEffect]

};


const INITIAL_VIEW_STATE = {

 longitude: 126.9779692,

 latitude: 37.566535,

 zoom: 6,

 pitch: 0,

 bearing: 0

};


const landCover = [[[-74.0, 40.7], [-74.02, 40.7], [-74.02, 40.72], [-74.0, 40.72]]];


const MS_PER_DAY = 8.64e7; // milliseconds in a day


const dataFilter = new DataFilterExtension({filterSize: 1});


export default class App extends Component {

 constructor(props) {

   super(props);

   this.state1 = {

     time: 0

   };


   const timeRange = this._getTimeRange(props.data);


   this.state = {


     timeRange,

     filterValue: timeRange,

     hoveredObject: null,

   };

   this._onHover = this._onHover.bind(this);

   this._renderTooltip = this._renderTooltip.bind(this);

 }


 componentWillReceiveProps(nextProps) {

   if (nextProps.data !== this.props.data) {

     const timeRange = this._getTimeRange(nextProps.data);

     this.setState({timeRange, filterValue: timeRange});

   }

 }


 componentDidMount() {

   this._animate();

 }


 componentWillUnmount() {

   if (this._animationFrame) {

     window.cancelAnimationFrame(this._animationFrame);

   }

 }



 _animate() {

   const {

     loopLength = 1000, // unit corresponds to the timestamp in source data

     animationSpeed = 20 // unit time per second

   } = this.props;

   const timestamp = Date.now() / 1000;

   const loopTime = loopLength / animationSpeed;


   this.setState({

     time: ((timestamp % loopTime) / loopTime) * loopLength

   });

   this._animationFrame = window.requestAnimationFrame(this._animate.bind(this));

 }


 _getTimeRange(data) {

   if (!data) {

     return null;

   }

   return data.reduce(

     (range, d) => {

       const t = d.timestamp / MS_PER_DAY;

       range[0] = Math.min(range[0], t);

       range[1] = Math.max(range[1], t);

       return range;

     },

     [Infinity, -Infinity]

   );

 }


 _onHover({x, y, object}) {

   this.setState({x, y, hoveredObject: object});

 }


 _renderLayers() {

   const {

     buildings = DATA_URL1.BUILDINGS,

     trips = DATA_URL1.TRIPS,

     trailLength = 30,

     theme = DEFAULT_THEME

   } = this.props;


   const {data} = this.props;

   const {filterValue} = this.state;



   return [

     data &&

     new ScatterplotLayer({

       id: 'earthquakes',

       data,

       opacity: 0.8,

       radiusScale: 1,

       radiusMinPixels: 1,

       wrapLongitude: true,


       getPosition: d => [d.longitude, d.latitude, -d.depth * 1000],

       getRadius: d => d.VisitingTime * 200,

       getFillColor: d => {

         const r = Math.sqrt(Math.max(d.depth, 0));

         return [255 - r * 15, r * 5, r * 10];

       },


       getFilterValue: d => d.timestamp / MS_PER_DAY, // in days

       filterRange: [filterValue[0], filterValue[1]],

       filterSoftRange: [

         filterValue[0] * 0.9 + filterValue[1] * 0.1,

         filterValue[0] * 0.1 + filterValue[1] * 0.9

       ],

       extensions: [dataFilter],


       pickable: true,

       onHover: this._onHover

     }),


     new PolygonLayer({

       id: 'ground',

       data: landCover,

       getPolygon: f => f,

       stroked: false,

       getFillColor: [0, 0, 0, 0]

     }),

     new TripsLayer({

       id: 'trips',

       data: trips,

       getPath: d => d.path,

       getTimestamps: d => d.timestamps,

       getColor: d => (d.vendor === 0 ? theme.trailColor0 : theme.trailColor1),

       opacity: 0.3,

       widthMinPixels: 2,

       rounded: true,

       trailLength,

       currentTime: this.state.time,


       shadowEnabled: false

     }),

     new PolygonLayer({

       id: 'buildings',

       data: buildings,

       extruded: true,

       wireframe: false,

       opacity: 0.5,

       getPolygon: f => f.polygon,

       getElevation: f => f.height,

       getFillColor: theme.buildingColor,

       material: theme.material

     })





   ];

 }


 _renderTooltip() {

   const {x, y, hoveredObject} = this.state;

   return (

     hoveredObject && (

       <div className="tooltip" style={{top: y, left: x}}>

         <div>

           <b>Time: </b>

           <span>{new Date(hoveredObject.timestamp).toUTCString()}</span>

         </div>

         <div>

           <b>VisitingTime: </b>

           <span>{hoveredObject.VisitingTime}</span>

         </div>

         <div>

           <b>Depth: </b>

           <span>{hoveredObject.depth} km</span>

         </div>

       </div>

     )

   );

 }


 _formatLabel(t) {

   const date = new Date(t * MS_PER_DAY);

   return `${date.getUTCFullYear()}/${date.getUTCMonth() + 1}`;

 }


 render() {

   const {

     viewState,

     mapStyle = 'mapbox://styles/mapbox/light-v9',

     theme = DEFAULT_THEME

   } = this.props;

   const {timeRange, filterValue} = this.state;



   return (

     <Fragment>

     <DeckGL

     views={MAP_VIEW}

       layers={this._renderLayers()}

       effects={theme.effects}

       initialViewState={INITIAL_VIEW_STATE}

       viewState={viewState}

       controller={true}

     >

       <StaticMap

         reuseMaps

         mapStyle={mapStyle}

         preventStyleDiffing={true}

         mapboxApiAccessToken={MAPBOX_TOKEN}

       />

       {this._renderTooltip}


     </DeckGL>


     {timeRange && (

         <RangeInput

           min={timeRange[0]}

           max={timeRange[1]}

           value={filterValue}

           formatLabel={this._formatLabel}

           onChange={({value}) => this.setState({filterValue: value})}

         />

       )}

       </Fragment>

   );

 }

}


export function renderToDOM(container) {

 require('d3-request').csv(DATA_URL, (error, response) => {

   if (!error) {

     const data = response.map(row => ({

       timestamp: new Date(`${row.DateTime} UTC`).getTime(),

       latitude: Number(row.Latitude),

       longitude: Number(row.Longitude),

       depth: Number(row.Depth),

       VisitingTime: Number(row.VisitingTime)

     }));

     render(<App data={data} />, container);

   }

 });


}

 

 

 

 

(2)下面代码和效果截图是我把setstate放在setinterval里面,添加了一个计时器,实现一个叫 tick() 的方法,计时器组件每秒都会调用它,但是,ScatterplotLayer散点图层的animate还是没有动起来。可以在浏览器的开发者工具里面看到time是实时更新的,但是filterValue没有更新(filterValue:Called to retrieve the value for each object that it will be filtered by. Returns either a number (if filterSize: 1) or an array. 这里是filter by timestamp)。我觉得可以设置计时器,就能实现animate呀,可能还有一小步没有设置正确。非常感谢您的指教~

 import React, {Component, Fragment} from 'react';

import {render} from 'react-dom';

import {StaticMap} from 'react-map-gl';

import {AmbientLight, PointLight, LightingEffect} from '@deck.gl/core';

import DeckGL from '@deck.gl/react';

import {PolygonLayer} from '@deck.gl/layers';

import {TripsLayer} from '@deck.gl/geo-layers';

import {ScatterplotLayer} from '@deck.gl/layers';

import {DataFilterExtension} from '@deck.gl/extensions';

import {MapView} from '@deck.gl/core';



// Set your mapbox token here

const MAPBOX_TOKEN = process.env.MapboxAccessToken; // eslint-disable-line


// Source data CSV

const DATA_URL1 = {

 TRIPS:

   './package1.json' // eslint-disable-line

};

const DATA_URL =

 './data2.csv'; // eslint-disable-line


const MAP_VIEW = new MapView({

   // 1 is the distance between the camera and the ground

   farZMultiplier: 100

 });


const ambientLight = new AmbientLight({

 color: [122, 122, 122],

 intensity: 1.0

});


const pointLight = new PointLight({

 color: [255, 255, 255],

 intensity: 2.0,

 position: [127.05, 37.5, 8000]

});


const lightingEffect = new LightingEffect({ambientLight, pointLight});


const material = {

 ambient: 0.1,

 diffuse: 0.6,

 shininess: 32,

 specularColor: [60, 64, 70]

};


const DEFAULT_THEME = {

 buildingColor: [74, 80, 87],

 trailColor0: [253, 128, 93],

 trailColor1: [23, 184, 190],

 material,

 effects: [lightingEffect]

};


const INITIAL_VIEW_STATE = {

 longitude: 126.9779692,

 latitude: 37.566535,

 zoom: 6,

 pitch: 0,

 bearing: 0

};


const landCover = [[[-74.0, 40.7], [-74.02, 40.7], [-74.02, 40.72], [-74.0, 40.72]]];


const MS_PER_DAY = 8.64e7; // milliseconds in a day


const dataFilter = new DataFilterExtension({filterSize: 1});


export default class App extends Component {

 constructor(props) {

   super(props);

   this.state = {date: new Date()};


   const timeRange = this._getTimeRange(props.data);


   this.state = {

     timeRange,

     filterValue: timeRange,


   };


 }


 componentWillReceiveProps(nextProps) {

   if (nextProps.data !== this.props.data) {

     const timeRange = this._getTimeRange(nextProps.data);

     this.setState({timeRange, filterValue: timeRange});

   }

 }


 componentDidMount() {

   this._animate();

   this.timerID = setInterval(

     () => this.tick(),

     1000

     );

 }


 componentWillUnmount() {

   if (this._animationFrame) {

     window.cancelAnimationFrame(this._animationFrame);

     clearInterval(this.timerID);

   }

 }

 tick() {

   this.setState({

     date: new Date()

   });

 }



 _animate() {

   const {

     loopLength = 1000, // unit corresponds to the timestamp in source data

     animationSpeed = 20 // unit time per second

   } = this.props;

   const timestamp = Date.now() / 1000;

   const loopTime = loopLength / animationSpeed;


   this.setState({

     time: ((timestamp % loopTime) / loopTime) * loopLength

   });

   this._animationFrame = window.requestAnimationFrame(this._animate.bind(this));

 }


 _getTimeRange(data) {

   if (!data) {

     return null;

   }

   return data.reduce(

     (range, d) => {

       const t = d.timestamp / MS_PER_DAY;

       range[0] = Math.min(range[0], t);

       range[1] = Math.max(range[1], t);

       return range;

     },

     [Infinity, -Infinity]

   );

 }




 _renderLayers() {

   const {

     buildings = DATA_URL1.BUILDINGS,

     trips = DATA_URL1.TRIPS,

     trailLength = 30,

     theme = DEFAULT_THEME

   } = this.props;


   const {data} = this.props;

   const {filterValue} = this.state;



   return [

     data &&

     new ScatterplotLayer({

       id: 'ScatterplotLayer',

       data,

       opacity: 0.8,

       radiusScale: 1,

       radiusMinPixels: 1,

       wrapLongitude: true,

       rounded: true,

       getTimestamps: d => d.timestamps,

       getPosition: d => [d.longitude, d.latitude],

       getRadius: d => d.VisitingTime * 200,

       getFillColor: d => {

         const r = Math.sqrt(Math.max(d.depth, 0));

         return [255 - r * 15, r * 5, r * 10];

       },


       getFilterValue: d => d.timestamp / MS_PER_DAY, // in days

       currentTime: this.state.time,

       filterRange: [filterValue[0], filterValue[1]],

       filterSoftRange: [

         filterValue[0] * 0.9 + filterValue[1] * 0.1,

         filterValue[0] * 0.1 + filterValue[1] * 0.9

       ],

       extensions: [dataFilter]



     }),


     new PolygonLayer({

       id: 'ground',

       data: landCover,

       getPolygon: f => f,

       stroked: false,

       getFillColor: [0, 0, 0, 0]

     }),

     new TripsLayer({

       id: 'trips',

       data: trips,

       getPath: d => d.path,

       getTimestamps: d => d.timestamps,

       getColor: d => (d.vendor === 0 ? theme.trailColor0 : theme.trailColor1),

       opacity: 0.3,

       widthMinPixels: 2,

       rounded: true,

       trailLength,

       currentTime: this.state.time,

       shadowEnabled: false

     }),

     new PolygonLayer({

       id: 'buildings',

       data: buildings,

       extruded: true,

       wireframe: false,

       opacity: 0.5,

       getPolygon: f => f.polygon,

       getElevation: f => f.height,

       getFillColor: theme.buildingColor,

       material: theme.material

     })





   ];

 }




 render() {

   const {

     viewState,

     mapStyle = 'mapbox://styles/mapbox/light-v9',

     theme = DEFAULT_THEME

   } = this.props;




   return (

     <Fragment>

     <DeckGL

     views={MAP_VIEW}

       layers={this._renderLayers()}

       effects={theme.effects}

       initialViewState={INITIAL_VIEW_STATE}

       viewState={viewState}

       controller={true}

     >

       <StaticMap

         reuseMaps

         mapStyle={mapStyle}

         preventStyleDiffing={true}

         mapboxApiAccessToken={MAPBOX_TOKEN}

       />


     </DeckGL>


     </Fragment>


   );

 }

}


export function renderToDOM(container) {

 require('d3-request').csv(DATA_URL, (error, response) => {

   if (!error) {

     const data = response.map(row => ({

       timestamp: new Date(`${row.DateTime} UTC`).getTime(),

       latitude: Number(row.Latitude),

       longitude: Number(row.Longitude),

       depth: Number(row.Depth),

       VisitingTime: Number(row.VisitingTime)

     }));

     render(<App data={data} />, container);

   }

 });


}

  • 写回答

3条回答 默认 最新

  • Qian�Ao Rigele 2020-05-23 17:34
    关注

    有熟悉react的大牛吗

    评论

报告相同问题?

悬赏问题

  • ¥15 用C语言输入方程怎么
  • ¥15 网站显示不安全连接问题
  • ¥15 github训练的模型参数无法下载
  • ¥15 51单片机显示器问题
  • ¥20 关于#qt#的问题:Qt代码的移植问题
  • ¥50 求图像处理的matlab方案
  • ¥50 winform中使用edge的Kiosk模式
  • ¥15 关于#python#的问题:功能监听网页
  • ¥15 怎么让wx群机器人发送音乐
  • ¥15 fesafe材料库问题