Skip to content

Commit

Permalink
优化点聚合组件
Browse files Browse the repository at this point in the history
  • Loading branch information
qiuxiang committed Dec 14, 2021
1 parent 552dbb4 commit 4643eeb
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 42 deletions.
9 changes: 3 additions & 6 deletions example/screens/cluster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ export default class Clustering extends Component {
this.status = nativeEvent;
this.cluster?.update(nativeEvent);
}}
onLongPress={() => {
this.setState({ markers: generateMarkers() });
setTimeout(() => this.cluster?.update(this.status!), 0);
}}
onLongPress={() => this.setState({ markers: generateMarkers() })}
>
<Cluster
onPress={({ position }) => {
Expand All @@ -49,8 +46,8 @@ export default class Clustering extends Component {
}
}

function generateMarkers() {
return Array(1000)
function generateMarkers(count = 1000) {
return Array(count)
.fill(0)
.map((_, i) => ({
position: { latitude: 39.5 + Math.random(), longitude: 116 + Math.random() },
Expand Down
120 changes: 84 additions & 36 deletions lib/src/cluster/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,65 @@ import { LatLng } from "../types";
import ClusterView from "./cluster-view";

export interface ClusterParams {
/**
* 唯一标识
*/
id: number;

/**
* 包含的 Marker 数量
*/
count: number;

/**
* 坐标
*/
position: LatLng;
}

interface MarkerItem {
/**
* 坐标
*/
position: LatLng;

/**
* 携带的数据,可以是任意类型
*/
properties?: any;
}

interface Props {
radius?: number;

/**
* 聚合点样式
*/
clusterStyle?: ViewStyle;

/**
* 聚合点文本样式
*/
clusterTextStyle?: ViewStyle;

/**
* 坐标点列表
*/
points: MarkerItem[];

/**
* 渲染 Marker
*/
renderMarker: (item: MarkerItem) => React.ReactNode;

/**
* 渲染聚合点
*/
renderCluster?: (params: ClusterParams) => React.ComponentType<any>;

/**
* 聚合点点击事件
*/
onPress?: (params: ClusterParams) => void;
}

Expand All @@ -33,60 +75,66 @@ interface State {
export default class Cluster extends React.PureComponent<Props, State> {
static defaultProps = { radius: 200 };
state: State = { clusters: [] };
status?: CameraEvent;
cluster?: Supercluster<any, ClusterProperties>;

componentDidMount() {
this.init(this.props);
this.init();
}

componentDidUpdate(props: Props) {
if (props.points != this.props.points) {
this.init(props);
this.init();
}
}

init(props: Props) {
async init() {
const { radius, points } = this.props;
// 如果主线程占用太多计算资源,会导致 ios onLoad 事件无法触发,非常蛋疼
// 暂时想到的解决办法是丢到下一个事件循环,但这可能会导致 update 失败
setTimeout(() => {
const { radius } = props;
const options = { radius, minZoom: 3, maxZoom: 21 };
this.cluster = new Supercluster<any, ClusterProperties>(options).load(
props.points.map((marker) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: [marker.position.longitude, marker.position.latitude],
},
properties: marker.properties,
}))
);
}, 0);
// 暂时想到的解决办法是等一个事件循环
await new Promise((resolve) => setTimeout(resolve, 0));
const options = { radius, minZoom: 3, maxZoom: 21 };
this.cluster = new Supercluster<any, ClusterProperties>(options).load(
points.map((marker) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: [marker.position.longitude, marker.position.latitude],
},
properties: marker.properties,
}))
);
if (this.status) {
this.update(this.status);
}
}

/**
* 需要在 MapView.onCameraIdle({ nativeEvent }) 回调里调用,参数为 nativeEvent
*/
update({ cameraPosition, latLngBounds }: CameraEvent) {
setTimeout(() => {
const { southwest, northeast } = latLngBounds;
const clusters = this.cluster!.getClusters(
[southwest.longitude, southwest.latitude, northeast.longitude, northeast.latitude],
Math.round(cameraPosition.zoom!)
);
this.setState({ clusters });
}, 0);
async update(status: CameraEvent) {
this.status = status;
await new Promise((resolve) => setTimeout(resolve, 0));
const { cameraPosition, latLngBounds } = status;
const { southwest, northeast } = latLngBounds;
const clusters = this.cluster!.getClusters(
[southwest.longitude, southwest.latitude, northeast.longitude, northeast.latitude],
Math.round(cameraPosition.zoom!)
);
this.setState({ clusters });
}

renderCluster = (cluster: ClusterParams) => (
<ClusterView
key={cluster.id}
cluster={cluster}
onPress={this.props.onPress}
style={this.props.clusterStyle}
textStyle={this.props.clusterTextStyle}
/>
);
renderCluster = (cluster: ClusterParams) => {
return (
<ClusterView
key={cluster.id}
cluster={cluster}
onPress={this.props.onPress}
style={this.props.clusterStyle}
textStyle={this.props.clusterTextStyle}
/>
);
};

render() {
const { renderCluster, renderMarker } = this.props;
Expand Down

0 comments on commit 4643eeb

Please sign in to comment.