antv-l7/src/core/layer.js

639 lines
18 KiB
JavaScript
Raw Normal View History

2018-10-23 19:32:42 +08:00
/**
* @fileOverview Layer基类
* @author lzx199065@gmail.com
*/
import Base from './base';
import * as THREE from './three';
import ColorUtil from '../attr/color-util';
2019-02-25 23:56:37 +08:00
import source from './source';
2018-10-23 19:32:42 +08:00
import PickingMaterial from '../core/engine/picking/pickingMaterial';
import Attr from '../attr/index';
import Util from '../util';
import Global from '../global';
2018-10-23 19:32:42 +08:00
let id = 1;
function parseFields(field) {
if (Util.isArray(field)) {
return field;
}
if (Util.isString(field)) {
return field.split('*');
}
return [ field ];
}
export default class Layer extends Base {
getDefaultCfg() {
return {
visible: true,
zIndex: 0,
type: '',
minZoom: 0,
maxZoom: 22,
rotation: 0,
attrOptions: {
},
scales: {},
attrs: {},
// 样式配置项
styleOptions: {
stroke: 'none',
2018-10-23 19:32:42 +08:00
strokeWidth: 1.0,
2018-11-08 20:56:17 +08:00
opacity: 1.0,
strokeOpacity: 1.0,
2018-11-16 16:52:25 +08:00
texture: false
2018-10-23 19:32:42 +08:00
},
destroyed: false,
2018-10-23 19:32:42 +08:00
// 选中时的配置项
selectedOptions: null,
// active 时的配置项
activedOptions: null,
2018-11-08 20:56:17 +08:00
animateOptions: {
2018-11-16 16:52:25 +08:00
enable: false
2018-11-08 20:56:17 +08:00
}
2018-10-23 19:32:42 +08:00
};
}
constructor(scene, cfg) {
super(cfg);
this.scene = scene;
this.map = scene.map;
this._object3D = new THREE.Object3D();
this._pickObject3D = new THREE.Object3D();
this._object3D.visible = this.get('visible');
this._object3D.renderOrder = this.get('zIndex') || 0;
const layerId = this._getUniqueId();
this.layerId = layerId;
this._activeIds = null;
2019-02-12 20:21:14 +08:00
const world = scene._engine.world;
world.add(this._object3D);
2018-10-23 19:32:42 +08:00
this.layerMesh = null;
this.layerLineMesh = null;
this._initEvents();
2018-10-23 19:32:42 +08:00
}
/**
2018-10-30 11:22:18 +08:00
* 将图层添加加到 Object
* @param {*} object three 物体
* @param {*} type mesh类型是区别是填充还是边线
2018-10-23 19:32:42 +08:00
*/
add(object, type = 'fill') {
type === 'fill' ? this.layerMesh = object : this.layerLineMesh = object;
2018-11-16 16:52:25 +08:00
this._visibleWithZoom();
this._zoomchangeHander = this._visibleWithZoom.bind(this);
this.scene.on('zoomchange', this._zoomchangeHander);
2018-11-19 12:06:42 +08:00
object.onBeforeRender = () => {
2018-11-16 16:52:25 +08:00
const zoom = this.scene.getZoom();
2019-03-01 13:09:41 +08:00
object.material.setUniformsValue('u_time', this.scene._engine.clock.getElapsedTime());
object.material.setUniformsValue('u_zoom', zoom);
this._preRender();
2018-11-16 16:52:25 +08:00
};
2018-11-03 16:39:22 +08:00
// 更新
2018-11-08 20:56:17 +08:00
if (this._needUpdateFilter) {
this._updateFilter(object);
2018-11-03 16:39:22 +08:00
}
2018-10-23 19:32:42 +08:00
this._object3D.add(object);
if (type === 'fill') { this._addPickMesh(object); }
2018-10-23 19:32:42 +08:00
}
remove(object) {
this._object3D.remove(object);
}
_getUniqueId() {
return id++;
}
_visible(visible) {
this.set('visible', visible);
this._object3D.visible = this.get('visible');
}
source(data, cfg = {}) {
cfg.data = data;
2019-02-26 17:22:55 +08:00
cfg.mapType = this.scene.mapType;
2019-02-25 23:56:37 +08:00
this.layerSource = new source(cfg);
// this.scene.workerPool.runTask({
// command: 'geojson',
// data: cfg
// }).then(data => {
// console.log(data);
// });
2018-10-23 19:32:42 +08:00
return this;
}
color(field, values) {
2018-11-08 20:56:17 +08:00
this._needUpdateColor = true;// 标识颜色是否需要更新
2018-10-23 19:32:42 +08:00
this._createAttrOption('color', field, values, Global.colors);
return this;
}
size(field, values) {
const fields = parseFields(field);
if (fields.indexOf('zoom') !== -1) {
this._zoomScale = true;
}
if (Util.isArray(fields) && !values) values = fields;
this._createAttrOption('size', field, values, Global.size);
return this;
}
shape(field, values) {
if (field.split(':').length === 2) {
this.shapeType = field.split(':')[0];
field = field.split(':')[1];
}
values === 'text' ? this.shapeType = values : null;
this._createAttrOption('shape', field, values, Global.shape);
2018-10-23 19:32:42 +08:00
return this;
}
/**
* 是否允许使用默认的图形激活交互
* @param {Boolean} enable 是否允许激活开关
* @param {Object} cfg 激活的配置项
* @return {Geom} 返回 geom 自身
*/
active(enable, cfg) {
if (enable === false) {
this.set('allowActive', false);
} else if (Util.isObject(enable)) {
this.set('allowActive', true);
this.set('activedOptions', enable);
} else {
this.set('allowActive', true);
this.set('activedOptions', cfg || { fill: Global.activeColor });
}
return this;
}
style(field, cfg) {
2018-11-16 16:52:25 +08:00
const colorItem = [ 'fill', 'stroke', 'color', 'baseColor', 'brightColor', 'windowColor' ];
2018-10-23 19:32:42 +08:00
let styleOptions = this.get('styleOptions');
if (!styleOptions) {
styleOptions = {};
this.set('styleOptions', styleOptions);
}
if (Util.isObject(field)) {
cfg = field;
field = null;
}
let fields;
if (field) {
fields = parseFields(field);
}
styleOptions.fields = fields;
Util.assign(styleOptions, cfg);
for (const item in cfg) {
if (colorItem.indexOf(item) !== -1 && styleOptions[item] !== 'none') {
2018-10-23 19:32:42 +08:00
styleOptions[item] = ColorUtil.color2RGBA(styleOptions[item]);
}
}
this.set('styleOptions', styleOptions);
return this;
}
filter(field, values) {
2018-11-03 16:39:22 +08:00
this._needUpdateFilter = true;
2018-10-23 19:32:42 +08:00
this._createAttrOption('filter', field, values, true);
return this;
}
2018-11-16 16:52:25 +08:00
animate(field, cfg) {
let animateOptions = this.get('animateOptions');
if (!animateOptions) {
animateOptions = {};
2018-11-08 20:56:17 +08:00
this.set('animateOptions', animateOptions);
}
if (Util.isObject(field)) {
cfg = field;
field = null;
}
let fields;
if (field) {
fields = parseFields(field);
}
2018-11-16 16:52:25 +08:00
animateOptions.fields = fields;
Util.assign(animateOptions, cfg);
this.set('animateOptions', animateOptions);
2018-10-23 19:32:42 +08:00
return this;
}
texture() {
}
hide() {
this._visible(false);
return this;
}
show() {
this._visible(true);
return this;
}
_createScale(field) {
const scales = this.get('scales');
let scale = scales[field];
if (!scale) {
scale = this.layerSource.createScale(field);
scales[field] = scale;
}
return scale;
}
_setAttrOptions(attrName, attrCfg) {
const options = this.get('attrOptions');
if (attrName === 'size' && this._zoomScale) {
attrCfg.zoom = this.map.getZoom();
}
options[attrName] = attrCfg;
}
_createAttrOption(attrName, field, cfg, defaultValues) {
const attrCfg = {};
attrCfg.field = field;
if (cfg) {
if (Util.isFunction(cfg)) {
attrCfg.callback = cfg;
} else {
attrCfg.values = cfg;
}
} else if (attrName !== 'color') {
attrCfg.values = defaultValues;
}
this._setAttrOptions(attrName, attrCfg);
}
2018-11-03 16:39:22 +08:00
// 初始化图层
2018-10-23 19:32:42 +08:00
init() {
this._initAttrs();
this._scaleByZoom();
this._mapping();
2018-11-08 20:56:17 +08:00
2018-10-23 19:32:42 +08:00
const activeHander = this._addActiveFeature.bind(this);
const resetHander = this._resetStyle.bind(this);
2018-10-23 19:32:42 +08:00
if (this.get('allowActive')) {
2018-12-24 16:15:18 +08:00
this.on('mousemove', activeHander);
this.on('mouseleave', resetHander);
2018-10-23 19:32:42 +08:00
} else {
2018-12-24 16:15:18 +08:00
this.off('mousemove', activeHander);
this.off('mouseleave', resetHander);
2018-10-23 19:32:42 +08:00
}
}
_addActiveFeature(e) {
const { featureId } = e;
if (featureId < 0) return;
2018-10-23 19:32:42 +08:00
const activeStyle = this.get('activedOptions');
2019-02-25 23:56:37 +08:00
// const selectFeatureIds = this.layerSource.getSelectFeatureId(featureId);
// 如果数据不显示状态则不进行高亮
2019-02-25 23:56:37 +08:00
if (this.layerData[featureId].hasOwnProperty('filter') && this.layerData[featureId].filter === false) { return; }
const style = Util.assign({}, this.layerData[featureId]);
2018-10-23 19:32:42 +08:00
style.color = ColorUtil.toRGB(activeStyle.fill).map(e => e / 255);
2018-11-19 12:06:42 +08:00
this.updateStyle([ featureId ], style);
2018-10-23 19:32:42 +08:00
}
_initAttrs() {
const attrOptions = this.get('attrOptions');
for (const type in attrOptions) {
if (attrOptions.hasOwnProperty(type)) {
2018-11-19 12:06:42 +08:00
this._updateAttr(type);
2018-11-03 16:39:22 +08:00
}
}
}
2018-11-19 12:06:42 +08:00
_updateAttr(type) {
2018-11-03 16:39:22 +08:00
const self = this;
const attrs = this.get('attrs');
const attrOptions = this.get('attrOptions');
const option = attrOptions[type];
option.neadUpdate = true;
const className = Util.upperFirst(type);
const fields = parseFields(option.field);
const scales = [];
for (let i = 0; i < fields.length; i++) {
const field = fields[i];
const scale = self._createScale(field);
2018-10-23 19:32:42 +08:00
2018-11-03 16:39:22 +08:00
if (type === 'color' && Util.isNil(option.values)) { // 设置 color 的默认色值
option.values = Global.colors;
2018-10-23 19:32:42 +08:00
}
2018-11-03 16:39:22 +08:00
scales.push(scale);
2018-10-23 19:32:42 +08:00
}
2018-11-03 16:39:22 +08:00
option.scales = scales;
const attr = new Attr[className](option);
attrs[type] = attr;
2018-10-23 19:32:42 +08:00
}
_updateSize(zoom) {
const sizeOption = this.get('attrOptions').size;
const fields = parseFields(sizeOption.field);
2019-02-25 23:56:37 +08:00
const data = this.layerSource.data.dataArray;
2018-10-23 19:32:42 +08:00
if (!this.zoomSizeCache) this.zoomSizeCache = {};
if (!this.zoomSizeCache[zoom]) {
this.zoomSizeCache[zoom] = [];
for (let i = 0; i < data.length; i++) {
const params = fields.map(field => data[i][field]);
const indexZoom = fields.indexOf('zoom');
indexZoom !== -1 ? params[indexZoom] = zoom : null;
this.zoomSizeCache[zoom].push(sizeOption.callback(...params));
}
}
this.emit('sizeUpdated', this.zoomSizeCache[zoom]);
}
_mapping() {
const self = this;
const attrs = self.get('attrs');
const mappedData = [];
2019-02-25 23:56:37 +08:00
// const data = this.layerSource.propertiesData;
const data = this.layerSource.data.dataArray;
2018-10-23 19:32:42 +08:00
for (let i = 0; i < data.length; i++) {
const record = data[i];
const newRecord = {};
newRecord.id = data[i]._id;
for (const k in attrs) {
if (attrs.hasOwnProperty(k)) {
const attr = attrs[k];
2018-11-03 16:39:22 +08:00
attr.needUpdate = false;
2018-10-23 19:32:42 +08:00
const names = attr.names;
const values = self._getAttrValues(attr, record);
if (names.length > 1) { // position 之类的生成多个字段的属性
for (let j = 0; j < values.length; j++) {
const val = values[j];
const name = names[j];
newRecord[name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
}
} else {
newRecord[names[0]] = values.length === 1 ? values[0] : values;
}
}
}
2019-02-25 23:56:37 +08:00
newRecord.coordinates = record.coordinates;
2018-10-23 19:32:42 +08:00
mappedData.push(newRecord);
}
2019-02-25 23:56:37 +08:00
this.layerData = mappedData;
2018-10-23 19:32:42 +08:00
}
2018-11-06 11:27:16 +08:00
// 更新地图映射
2018-11-03 16:39:22 +08:00
_updateMaping() {
2018-10-23 19:32:42 +08:00
const self = this;
const attrs = self.get('attrs');
2019-02-25 23:56:37 +08:00
const data = this.layerSource.data.dataArray;
2018-10-23 19:32:42 +08:00
for (let i = 0; i < data.length; i++) {
const record = data[i];
2018-11-03 16:39:22 +08:00
for (const attrName in attrs) {
if (attrs.hasOwnProperty(attrName) && attrs[attrName].neadUpdate) {
const attr = attrs[attrName];
const names = attr.names;
const values = self._getAttrValues(attr, record);
if (names.length > 1) { // position 之类的生成多个字段的属性
for (let j = 0; j < values.length; j++) {
const val = values[j];
const name = names[j];
2019-02-25 23:56:37 +08:00
this.layerData[i][name] = (Util.isArray(val) && val.length === 1) ? val[0] : val; // 只有一个值时返回第一个属性值
2018-11-03 16:39:22 +08:00
}
} else {
2019-02-25 23:56:37 +08:00
this.layerData[i][names[0]] = values.length === 1 ? values[0] : values;
2018-10-23 19:32:42 +08:00
2018-11-03 16:39:22 +08:00
}
attr.neadUpdate = true;
2018-10-23 19:32:42 +08:00
}
}
}
}
// 获取属性映射的值
_getAttrValues(attr, record) {
const scales = attr.scales;
const params = [];
for (let i = 0; i < scales.length; i++) {
const scale = scales[i];
const field = scale.field;
if (scale.type === 'identity') {
params.push(scale.value);
} else {
params.push(record[field]);
}
}
const indexZoom = params.indexOf('zoom');
indexZoom !== -1 ? params[indexZoom] = attr.zoom : null;
const values = attr.mapping(...params);
return values;
}
// temp
_getDataType(data) {
if (data.hasOwnProperty('type')) {
const type = data.type;
if (type === 'FeatureCollection') {
return 'geojson';
}
}
return 'basic';
}
_scaleByZoom() {
if (this._zoomScale) {
this.map.on('zoomend', () => {
const zoom = this.map.getZoom();
this._updateSize(Math.floor(zoom));
});
}
}
2018-12-24 16:15:18 +08:00
// on(type, callback) {
2018-10-23 19:32:42 +08:00
2018-12-24 16:15:18 +08:00
// this._addPickingEvents();
// super.on(type, callback);
// }
2018-10-23 19:32:42 +08:00
getPickingId() {
2018-11-19 12:06:42 +08:00
return this.scene._engine._picking.getNextId();
2018-10-23 19:32:42 +08:00
}
addToPicking(object) {
this.scene._engine._picking.add(object);
}
removeFromPicking(object) {
this.scene._engine._picking.remove(object);
}
2018-11-19 12:06:42 +08:00
_addPickMesh(mesh) {
2018-10-23 19:32:42 +08:00
this._pickingMesh = new THREE.Object3D();
this._pickingMesh.name = this.layerId;
// this._visibleWithZoom();
// this.scene.on('zoomchange', () => {
// this._visibleWithZoom();
// });
2018-11-19 12:06:42 +08:00
2018-10-23 19:32:42 +08:00
this.addToPicking(this._pickingMesh);
2018-11-19 10:45:40 +08:00
const pickmaterial = new PickingMaterial({
2018-11-19 12:06:42 +08:00
u_zoom: this.scene.getZoom()
2018-11-19 10:45:40 +08:00
});
2018-11-20 20:07:22 +08:00
2018-11-20 15:04:21 +08:00
const pickingMesh = new THREE[mesh.type](mesh.geometry, pickmaterial);
pickingMesh.name = this.layerId;
2018-11-19 12:06:42 +08:00
pickmaterial.setDefinesvalue(this.type, true);
2018-11-20 15:04:21 +08:00
pickingMesh.onBeforeRender = () => {
2018-11-19 10:45:40 +08:00
const zoom = this.scene.getZoom();
2018-11-20 15:04:21 +08:00
pickingMesh.material.setUniformsValue('u_zoom', zoom);
2018-11-19 10:45:40 +08:00
};
2018-10-23 19:32:42 +08:00
this._pickingMesh.add(pickingMesh);
2019-02-25 23:56:37 +08:00
2018-10-23 19:32:42 +08:00
}
_setPickingId() {
this._pickingId = this.getPickingId();
}
_initEvents() {
this.scene.on('pick-' + this.layerId, e => {
2018-12-24 16:15:18 +08:00
const { featureId, point2d, type } = e;
if (featureId < -100 && this._activeIds !== null) {
this.emit('mouseleave');
return;
}
const feature = this.layerSource.getSelectFeature(featureId);
2018-10-31 18:01:56 +08:00
const lnglat = this.scene.containerToLngLat(point2d);
const target = {
featureId,
2018-10-31 18:01:56 +08:00
feature,
2018-11-19 12:06:42 +08:00
pixel: point2d,
lnglat: { lng: lnglat.lng, lat: lnglat.lat }
};
2018-12-24 16:15:18 +08:00
if (featureId >= 0) {
this.emit(type, target);
}
2018-10-23 19:32:42 +08:00
});
}
/**
* 更新active操作
* @param {*} featureStyleId 需要更新的要素Id
* @param {*} style 更新的要素样式
*/
updateStyle(featureStyleId, style) {
2018-11-19 12:06:42 +08:00
if (this._activeIds) {
this._resetStyle();
2018-11-19 12:06:42 +08:00
}
this._activeIds = featureStyleId;
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
const color = style.color;
const colorAttr = this.layerMesh.geometry.attributes.a_color;
const firstId = pickingId.indexOf(featureStyleId[0] + 1);
for (let i = firstId; i < pickingId.length; i++) {
2018-11-20 20:07:22 +08:00
if (pickingId[i] === featureStyleId[0] + 1) {
2018-11-19 12:06:42 +08:00
colorAttr.array[i * 4 + 0] = color[0];
colorAttr.array[i * 4 + 1] = color[1];
colorAttr.array[i * 4 + 2] = color[2];
colorAttr.array[i * 4 + 3] = color[3];
} else {
break;
2018-11-19 10:45:40 +08:00
}
2018-11-19 12:06:42 +08:00
}
colorAttr.needsUpdate = true;
return;
2018-11-03 16:39:22 +08:00
}
2018-11-19 10:45:40 +08:00
2018-11-19 12:06:42 +08:00
_updateColor() {
2018-11-03 16:39:22 +08:00
this._updateMaping();
2018-11-19 12:06:42 +08:00
2018-10-23 19:32:42 +08:00
}
/**
* 用于过滤数据
* @param {*} object 需要过滤的mesh
2018-10-23 19:32:42 +08:00
*/
_updateFilter(object) {
2018-11-03 16:39:22 +08:00
this._updateMaping();
2019-02-25 23:56:37 +08:00
const filterData = this.layerData;
2018-10-23 19:32:42 +08:00
this._activeIds = null; // 清空选中元素
const colorAttr = object.geometry.attributes.a_color;
const pickAttr = object.geometry.attributes.pickingId;
2018-11-19 12:06:42 +08:00
pickAttr.array.forEach((id, index) => {
id = Math.abs(id);
2019-02-25 23:56:37 +08:00
const color = [ ...this.layerData[id - 1].color ];
2018-11-19 10:45:40 +08:00
id = Math.abs(id);
2018-11-19 12:06:42 +08:00
const item = filterData[id - 1];
2018-11-19 10:45:40 +08:00
if (item.hasOwnProperty('filter') && item.filter === false) {
2018-11-19 12:06:42 +08:00
colorAttr.array[index * 4 + 0] = 0;
colorAttr.array[index * 4 + 1] = 0;
colorAttr.array[index * 4 + 2] = 0;
colorAttr.array[index * 4 + 3] = 0;
pickAttr.array[index] = -id; // 通过Id数据过滤 id<0 不显示
2018-11-19 10:45:40 +08:00
} else {
2018-11-19 12:06:42 +08:00
colorAttr.array[index * 4 + 0] = color[0];
colorAttr.array[index * 4 + 1] = color[1];
colorAttr.array[index * 4 + 2] = color[2];
colorAttr.array[index * 4 + 3] = color[3];
2018-11-19 10:45:40 +08:00
pickAttr.array[index] = id;
2018-10-23 19:32:42 +08:00
}
2018-11-19 12:06:42 +08:00
});
colorAttr.needsUpdate = true;
pickAttr.needsUpdate = true;
2018-10-23 19:32:42 +08:00
}
2018-11-19 12:06:42 +08:00
_visibleWithZoom() {
const zoom = this.scene.getZoom();
2018-11-16 16:52:25 +08:00
const minZoom = this.get('minZoom');
const maxZoom = this.get('maxZoom');
// z-fighting
2018-11-19 10:45:40 +08:00
let offset = 0;
2018-11-19 12:06:42 +08:00
if (this.type === 'point') {
2018-11-19 10:45:40 +08:00
offset = 5;
2018-11-19 12:06:42 +08:00
} else if (this.type === 'polyline') {
2018-11-19 10:45:40 +08:00
offset = 2;
}
2018-11-20 20:07:22 +08:00
this._object3D.position.z = offset * Math.pow(2, 20 - zoom);
2018-11-19 12:06:42 +08:00
if (zoom < minZoom || zoom > maxZoom) {
this._object3D.visible = false;
} else if (this.get('visible')) {
this._object3D.visible = true;
2018-11-16 16:52:25 +08:00
}
}
2018-10-23 19:32:42 +08:00
/**
* 重置高亮要素
*/
_resetStyle() {
2018-11-19 12:06:42 +08:00
const pickingId = this.layerMesh.geometry.attributes.pickingId.array;
const colorAttr = this.layerMesh.geometry.attributes.a_color;
2018-11-20 20:07:22 +08:00
this._activeIds.forEach(index => {
2019-02-25 23:56:37 +08:00
const color = this.layerData[index].color;
2018-11-19 12:06:42 +08:00
const firstId = pickingId.indexOf(index + 1);
for (let i = firstId; i < pickingId.length; i++) {
2018-11-20 20:07:22 +08:00
if (pickingId[i] === index + 1) {
2018-11-19 12:06:42 +08:00
colorAttr.array[i * 4 + 0] = color[0];
colorAttr.array[i * 4 + 1] = color[1];
colorAttr.array[i * 4 + 2] = color[2];
colorAttr.array[i * 4 + 3] = color[3];
2018-11-19 10:45:40 +08:00
}
2018-11-19 12:06:42 +08:00
}
});
colorAttr.needsUpdate = true;
this._activeIds = null;
2018-10-23 19:32:42 +08:00
}
2018-11-03 16:39:22 +08:00
/**
* 销毁Layer对象
*/
destroy() {
this.removeAllListeners();
2018-11-19 12:06:42 +08:00
if (this._object3D && this._object3D.children) {
2018-10-23 19:32:42 +08:00
let child;
2018-11-19 12:06:42 +08:00
for (let i = 0; i < this._object3D.children.length; i++) {
child = this._object3D.children[i];
if (!child) {
continue;
}
this.remove(child);
if (child.geometry) {
// child.geometry.dispose();
2018-11-19 12:06:42 +08:00
child.geometry = null;
}
if (child.material) {
2018-10-23 19:32:42 +08:00
if (child.material.map) {
child.material.map.dispose();
child.material.map = null;
}
child.material.dispose();
child.material = null;
}
child = null;
2018-10-23 19:32:42 +08:00
}
}
2018-11-19 12:06:42 +08:00
this._object3D = null;
this.scene._engine._scene.remove(this._object3D);
this.scene._engine._picking.remove(this._pickingMesh);
this.scene.off('zoomchange', this._zoomchangeHander);
this.destroyed = true;
2018-10-23 19:32:42 +08:00
}
2018-12-24 16:15:18 +08:00
_preRender() {
}
2018-10-23 19:32:42 +08:00
}