fix: delay first setOption until initial resize, update deps

This commit is contained in:
Justineo
2021-06-10 01:14:15 +08:00
parent 87d9f843ac
commit a77ecc726d
19 changed files with 5230 additions and 6284 deletions

View File

@ -3,7 +3,6 @@ import {
defineComponent,
unref,
shallowRef,
toRef,
toRefs,
watch,
computed,
@ -63,13 +62,8 @@ export default defineComponent({
...loadingProps
},
inheritAttrs: false,
created() {
console.log(this);
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// @ts-expect-error
// @ts-expect-error listeners for Vue 2 compatibility
setup(props, { attrs, listeners }) {
type T = Required<typeof props>;
const root = shallowRef<HTMLElement>();
const chart = shallowRef<EChartsType>();
const manualOption = shallowRef<Option>();
@ -78,9 +72,13 @@ export default defineComponent({
INIT_OPTIONS_KEY,
null
) as InitOptionsInjection;
const defaultUpdateOptions = inject(UPDATE_OPTIONS_KEY, {
lazyUpdate: true
}) as UpdateOptionsInjection;
const defaultUpdateOptions = inject(
UPDATE_OPTIONS_KEY,
null
) as UpdateOptionsInjection;
const { autoresize, manualUpdate, loading, loadingOptions } = toRefs(props);
const realOption = computed(
() => manualOption.value || props.option || Object.create(null)
);
@ -91,12 +89,6 @@ export default defineComponent({
const realUpdateOptions = computed(
() => props.updateOptions || unref(defaultUpdateOptions) || {}
);
const { autoresize, manualUpdate, loading } = toRefs(props);
const initOptions = toRef(props, "initOptions");
const loadingOptions = toRef(props, "loadingOptions");
const nonEventAttrs = computed(() => omitOn(attrs));
function init(option?: Option) {
@ -142,8 +134,6 @@ export default defineComponent({
}
});
instance.setOption(option || realOption.value, realUpdateOptions.value);
function resize() {
if (instance && !instance.isDisposed()) {
// temporarily suppress errors caused by https://github.com/apache/echarts/issues/14846
@ -159,10 +149,19 @@ export default defineComponent({
}
}
function commit() {
instance.setOption(option || realOption.value, realUpdateOptions.value);
}
if (autoresize.value) {
// Try to make chart fit to container in case container size
// is changed synchronously or in already queued microtasks
nextTick(resize);
nextTick(() => {
resize();
commit();
});
} else {
commit();
}
}
@ -217,7 +216,7 @@ export default defineComponent({
);
watch(
[realTheme, initOptions],
[realTheme, realInitOptions],
() => {
cleanup();
init();
@ -237,7 +236,7 @@ export default defineComponent({
useLoading(chart, loading, loadingOptions);
useAutoresize(chart, autoresize, root, realOption);
useAutoresize(chart, autoresize, root);
onMounted(() => {
if (props.option) {

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Ref, unref } from "vue-demi";
import { Ref } from "vue-demi";
import { EChartsType, Option } from "../types";
const METHOD_NAMES = [
@ -26,7 +26,7 @@ type PublicMethods = Pick<EChartsType, MethodName>;
export function usePublicAPI(
chart: Ref<EChartsType | undefined>,
init: (option?: Option) => void
) {
): PublicMethods {
function makePublicMethod<T extends MethodName>(
name: T
): (...args: Parameters<EChartsType[T]>) => ReturnType<EChartsType[T]> {
@ -42,12 +42,6 @@ export function usePublicAPI(
};
}
function makeAnyMethod<T extends MethodName>(
name: T
): (...args: any[]) => ReturnType<EChartsType[T]> {
return makePublicMethod(name) as any;
}
function makePublicMethods(): PublicMethods {
const methods = Object.create(null);
METHOD_NAMES.forEach(name => {
@ -57,10 +51,5 @@ export function usePublicAPI(
return methods as PublicMethods;
}
return {
...makePublicMethods(),
dispatchAction: makeAnyMethod("dispatchAction"),
getDataURL: makeAnyMethod("getDataURL"),
getConnectedDataURL: makeAnyMethod("getConnectedDataURL")
};
return makePublicMethods();
}

View File

@ -1,37 +1,19 @@
import { Ref, watch } from "vue-demi";
import { throttle } from "echarts/core";
import { addListener, removeListener, ResizeCallback } from "resize-detector";
import { EChartsType, Option } from "../types";
import { EChartsType } from "../types";
export function useAutoresize(
chart: Ref<EChartsType | undefined>,
autoresize: Ref<boolean>,
root: Ref<HTMLElement | undefined>,
option: Ref<Option>
root: Ref<HTMLElement | undefined>
): void {
let resizeListener: ResizeCallback | null = null;
let lastArea = 0;
function getArea() {
const el = root.value;
if (!el) {
return 0;
}
return el.offsetWidth * el.offsetHeight;
}
watch([root, chart, autoresize], ([root, chart, autoresize], _, cleanup) => {
if (root && chart && autoresize) {
lastArea = getArea();
resizeListener = throttle(() => {
if (lastArea === 0) {
chart.setOption(Object.create(null), true);
chart.resize();
chart.setOption(option.value, true);
} else {
chart.resize();
}
lastArea = getArea();
chart.resize();
}, 100);
addListener(root, resizeListener);
@ -39,7 +21,6 @@ export function useAutoresize(
cleanup(() => {
if (resizeListener && root) {
lastArea = 0;
removeListener(root, resizeListener);
}
});

View File

@ -3,14 +3,16 @@ import { EChartsType } from "../types";
export const LOADING_OPTIONS_KEY = "ecLoadingOptions";
type UnknownRecord = Record<string, unknown>;
export function useLoading(
chart: Ref<EChartsType | undefined>,
loading: Ref<boolean>,
loadingOptions: Ref<object | undefined>
loadingOptions: Ref<UnknownRecord | undefined>
): void {
const defaultLoadingOptions = inject(LOADING_OPTIONS_KEY, {}) as
| object
| Ref<object>;
| UnknownRecord
| Ref<UnknownRecord>;
const realLoadingOptions = computed(() => ({
...unref(defaultLoadingOptions),
...loadingOptions?.value

View File

@ -128,7 +128,7 @@
></button>
</h2>
<section v-if="expand.map">
<figure style="background-color: #404a59;">
<figure style="background-color: #404a59">
<v-chart
:option="map"
:init-options="initOptions"
@ -225,7 +225,7 @@
>
</p>
<p><button :disabled="flightLoaded" @click="loadFlights">Load</button></p>
<figure style="background-color: #003;">
<figure style="background-color: #003">
<v-chart
ref="flight"
:init-options="initOptions"
@ -274,7 +274,13 @@
import qs from "qs";
import VChart from "../ECharts";
import * as echarts from "echarts/core";
import {
use,
registerMap,
registerTheme,
connect,
disconnect
} from "echarts/core";
import {
BarChart,
LineChart,
@ -293,11 +299,26 @@ import {
LegendComponent,
TitleComponent,
VisualMapComponent,
DatasetComponent
DatasetComponent,
ToolboxComponent,
DataZoomComponent
} from "echarts/components";
import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
// import "echarts-liquidfill";
// import logo from "./data/logo";
import getBar from "./data/bar";
import pie from "./data/pie";
import polar from "./data/polar";
import scatter from "./data/scatter";
import map from "./data/map";
import { c1, c2 } from "./data/connect";
const { use, registerMap, registerTheme } = echarts;
// custom theme
import theme from "./theme.json";
// Map of China
import chinaMap from "./china.json";
import worldMap from "./world.json";
use([
BarChart,
@ -317,25 +338,11 @@ use([
VisualMapComponent,
DatasetComponent,
CanvasRenderer,
SVGRenderer
SVGRenderer,
ToolboxComponent,
DataZoomComponent
]);
// import "echarts-liquidfill";
// import logo from "./data/logo";
import getBar from "./data/bar";
import pie from "./data/pie";
import polar from "./data/polar";
import scatter from "./data/scatter";
import map from "./data/map";
import { c1, c2 } from "./data/connect";
// custom theme
import theme from "./theme.json";
// Map of China
import chinaMap from "./china.json";
import worldMap from "./world.json";
// registering map data
registerMap("china", chinaMap);
registerMap("world", worldMap);
@ -444,7 +451,7 @@ export default {
function getAirportCoord(idx) {
return [data.airports[idx][3], data.airports[idx][4]];
}
const routes = data.routes.map(function(airline) {
const routes = data.routes.map(airline => {
return [getAirportCoord(airline[1]), getAirportCoord(airline[2])];
});
@ -533,7 +540,11 @@ export default {
watch: {
connected: {
handler(value) {
echarts[value ? "connect" : "disconnect"]("radiance");
if (value) {
connect("radiance");
} else {
disconnect("radiance");
}
},
immediate: true
},

View File

@ -12,27 +12,27 @@ export default function getData() {
source: [
{
Product: "Matcha Latte",
"2015": random(),
"2016": random(),
"2017": random()
2015: random(),
2016: random(),
2017: random()
},
{
Product: "Milk Tea",
"2015": random(),
"2016": random(),
"2017": random()
2015: random(),
2016: random(),
2017: random()
},
{
Product: "Cheese Cocoa",
"2015": random(),
"2016": random(),
"2017": random()
2015: random(),
2016: random(),
2017: random()
},
{
Product: "Walnut Brownie",
"2015": random(),
"2016": random(),
"2017": random()
2015: random(),
2016: random(),
2017: random()
}
]
},

View File

@ -446,7 +446,7 @@ export default {
data: convertData(data),
symbolSize: val => val[2] / 10,
tooltip: {
formatter: function(val) {
formatter: function (val) {
return val.name + ": " + val.value[2];
}
},
@ -468,7 +468,7 @@ export default {
scale: true
},
tooltip: {
formatter: function(val) {
formatter: function (val) {
return val.name + ": " + val.value[2];
}
},

1
src/index.vue2.d.ts vendored
View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
declare const LOADING_OPTIONS_KEY = "ecLoadingOptions";
declare const THEME_KEY = "ecTheme";
declare const INIT_OPTIONS_KEY = "ecInitOptions";

View File

@ -6,9 +6,9 @@ type Attrs = {
// Copied from
// https://github.com/vuejs/vue-next/blob/5a7a1b8293822219283d6e267496bec02234b0bc/packages/shared/src/index.ts#L40-L41
const onRE = /^on[^a-z]/;
export const isOn = (key: string) => onRE.test(key);
export const isOn = (key: string): boolean => onRE.test(key);
export function omitOn(attrs: Attrs) {
export function omitOn(attrs: Attrs): Attrs {
const result: Attrs = {};
for (const key in attrs) {
if (!isOn(key)) {