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

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

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

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

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);

    }

  });

  

}

给TripsLayer添加一个拖动事件,当TripsLayer拖动时调用setState

大神,添加之后tripslayer也是需要拖动才能开始渲染出动画效果吗?现在想要的实现的是一打开网页之后tripslayer和ScatterplotLayer就能够自动开始播放动画效果。就像目前是只有tripslayer在自动播放,而ScatterplotLayer需要拖动下方的进度条才会播放,谢谢啦!

tripslayer自动播放是不是有一个控制它的函数,在这个函数里面调用setState

对滴,就是animate函数,调用了Setstate,如下:

 _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));

  }

然后,在renderLayers下面返回了currentTime: this.state.time,如下:

new TripsLayer({

        id: 'trips',

        data: trips,

        getPath: d => d.path,

        getTimestamps: d => d.timestam

        rounded: true,

        trailLength,

        currentTime: this.state.time,

请问怎样调整一下代码可以让animate函数把ScatterplotLayer也一起控制,那就太好了

那在renderLayers调用后再接着调用控制ScatterplotLayer的函数试试