提交 5239e3cf 编写于 作者: P pissang

feat: using state for all user interactions.

上级 7e84356b
......@@ -111,19 +111,6 @@ class Symbol extends graphic.Group {
return this.childAt(0) as ECSymbol;
}
/**
* Get scale(aka, current symbol size).
* Including the change caused by animation
*/
getScale() {
const symbolPath = this.childAt(0);
return [symbolPath.scaleX, symbolPath.scaleY];
}
getOriginalScale() {
return [this._scaleX, this._scaleY];
}
/**
* Highlight symbol
*/
......@@ -291,11 +278,20 @@ class Symbol extends graphic.Group {
this._scaleX = symbolSize[0] / 2;
this._scaleY = symbolSize[1] / 2;
symbolPath.onStateChange = (
hoverAnimation && seriesModel.isAnimationEnabled()
) ? onStateChange : null;
graphic.enableHoverEmphasis(symbolPath, hoverItemStyle);
symbolPath.ensureState('emphasis').style = hoverItemStyle;
if (hoverAnimation && seriesModel.isAnimationEnabled()) {
const scaleEmphasisState = this.ensureState('emphasis');
const scale = Math.max(1.1, 3 / this._scaleY + 1);
scaleEmphasisState.scaleX = scale;
scaleEmphasisState.scaleY = scale;
}
else {
this.states.emphasis = null;
}
graphic.enableHoverEmphasis(this);
}
fadeOut(cb: () => void, opt?: {
......@@ -330,33 +326,6 @@ class Symbol extends graphic.Group {
}
}
function onStateChange(this: ECSymbol, fromState: DisplayState, toState: DisplayState) {
// Do not support this hover animation util some scenario required.
// Animation can only be supported in hover layer when using `el.incremetal`.
if (this.incremental || this.useHoverLayer) {
return;
}
const scale = (this.parent as Symbol).getOriginalScale();
if (toState === 'emphasis') {
const ratio = scale[1] / scale[0];
const emphasisOpt = {
scaleX: Math.max(scale[0] * 1.1, scale[0] + 3),
scaleY: Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
};
// FIXME
// modify it after support stop specified animation.
// toState === fromState
// ? (this.stopAnimation(), this.attr(emphasisOpt))
this.animateTo(emphasisOpt, { duration: 400, easing: 'elasticOut' });
}
else if (toState === 'normal') {
this.animateTo({
scaleX: scale[0],
scaleY: scale[1]
}, { duration: 400, easing: 'elasticOut' });
}
}
function driftSymbol(this: ECSymbol, dx: number, dy: number) {
this.parent.drift(dx, dy);
......
......@@ -222,7 +222,7 @@ class PieSeriesModel extends SeriesModel<PieSeriesOption> {
// 选中时扇区偏移量
selectedOffset: 10,
// 高亮扇区偏移量
hoverOffset: 10,
hoverOffset: 5,
// If use strategy to avoid label overlapping
avoidLabelOverlap: true,
......
......@@ -24,11 +24,10 @@ import * as graphic from '../../util/graphic';
import ChartView from '../../view/Chart';
import GlobalModel from '../../model/Global';
import ExtensionAPI from '../../ExtensionAPI';
import { Payload, DisplayState, ECElement, ColorString } from '../../util/types';
import { Payload, ColorString } from '../../util/types';
import List from '../../data/List';
import PieSeriesModel, {PieDataItemOption} from './PieSeries';
import { Dictionary } from 'zrender/src/core/types';
import Element from 'zrender/src/Element';
import { ElementAnimateConfig } from 'zrender/src/Element';
function updateDataSelected(
this: PiePiece,
......@@ -40,7 +39,6 @@ function updateDataSelected(
const data = seriesModel.getData();
const dataIndex = graphic.getECData(this).dataIndex;
const name = data.getName(dataIndex);
const selectedOffset = seriesModel.get('selectedOffset');
api.dispatchAction({
type: 'pieToggleSelect',
......@@ -49,67 +47,38 @@ function updateDataSelected(
seriesId: seriesModel.id
});
const animationCfg: ElementAnimateConfig = {
duration: seriesModel.get('animation') ? 200 : 0,
easing: 'cubicOut'
};
data.each(function (idx) {
toggleItemSelected(
data.getItemGraphicEl(idx),
data.getItemLayout(idx),
seriesModel.isSelected(data.getName(idx)),
selectedOffset,
hasAnimation
);
const el = data.getItemGraphicEl(idx);
el.toggleState('select', seriesModel.isSelected(data.getName(idx)), animationCfg);
});
}
function toggleItemSelected(
el: Element,
layout: Dictionary<any>, // FIXME:TS make a type.
isSelected: boolean,
selectedOffset: number,
hasAnimation: boolean
): void {
const midAngle = (layout.startAngle + layout.endAngle) / 2;
const dx = Math.cos(midAngle);
const dy = Math.sin(midAngle);
const offset = isSelected ? selectedOffset : 0;
const obj = {
x: dx * offset,
y: dy * offset
};
hasAnimation
// animateTo will stop revious animation like update transition
? el.animate()
.when(200, obj)
.start('bounceOut')
: el.attr(obj);
}
/**
* Piece of pie including Sector, Label, LabelLine
*/
class PiePiece extends graphic.Group {
class PiePiece extends graphic.Sector {
constructor(data: List, idx: number) {
super();
const sector = new graphic.Sector({
z2: 2
});
this.z2 = 2;
const polyline = new graphic.Polyline();
const text = new graphic.Text();
this.add(sector);
this.add(polyline);
sector.setTextContent(text);
this.setTextGuideLine(polyline);
this.setTextContent(text);
this.updateData(data, idx, true);
}
updateData(data: List, idx: number, firstCreate?: boolean): void {
const sector = this.childAt(0) as graphic.Sector;
const sector = this;
const seriesModel = data.hostModel as PieSeriesModel;
const itemModel = data.getItemModel<PieDataItemOption>(idx);
......@@ -161,53 +130,50 @@ class PiePiece extends graphic.Group {
const sectorEmphasisState = sector.ensureState('emphasis');
sectorEmphasisState.style = itemModel.getModel(['emphasis', 'itemStyle']).getItemStyle();
const sectorSelectState = sector.ensureState('select');
const midAngle = (layout.startAngle + layout.endAngle) / 2;
const offset = seriesModel.get('selectedOffset');
const dx = Math.cos(midAngle) * offset;
const dy = Math.sin(midAngle) * offset;
sectorSelectState.x = dx;
sectorSelectState.y = dy;
sector.toggleState('select', seriesModel.isSelected(data.getName(idx)), {
duration: seriesModel.get('animation') ? 200 : 0,
easing: 'cubicOut'
});
const cursorStyle = itemModel.getShallow('cursor');
cursorStyle && sector.attr('cursor', cursorStyle);
// Toggle selected
toggleItemSelected(
this,
data.getItemLayout(idx),
seriesModel.isSelected(data.getName(idx)),
seriesModel.get('selectedOffset'),
seriesModel.get('animation')
);
// Label and text animation should be applied only for transition type animation when update
const withAnimation = !firstCreate && animationTypeUpdate === 'transition';
this._updateLabel(data, idx, withAnimation);
(this as ECElement).onStateChange = (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled())
? function (fromState: DisplayState, toState: DisplayState): void {
if (toState === 'emphasis') {
// Sector may has animation of updating data. Force to move to the last frame
// Or it may stopped on the wrong shape
sector.stopAnimation(true);
sector.animateTo({
shape: {
r: layout.r + seriesModel.get('hoverOffset')
}
}, { duration: 300, easing: 'elasticOut' });
}
else {
sector.stopAnimation(true);
sector.animateTo({
shape: {
r: layout.r
}
}, { duration: 300, easing: 'elasticOut' });
}
}
: null;
const emphasisState = sector.ensureState('emphasis');
emphasisState.shape = {
r: layout.r + itemModel.get('hoverAnimation') // TODO: Change a name.
? seriesModel.get('hoverOffset') : 0
};
const labelLine = sector.getTextGuideLine();
const labelText = sector.getTextContent();
const labelLineSelectState = labelLine.ensureState('select');
const labelTextSelectState = labelText.ensureState('select');
labelLineSelectState.x = dx;
labelLineSelectState.y = dy;
labelTextSelectState.x = dx;
labelTextSelectState.y = dy;
graphic.enableHoverEmphasis(this);
}
private _updateLabel(data: List, idx: number, withAnimation: boolean): void {
const sector = this.childAt(0);
const labelLine = this.childAt(1) as graphic.Polyline;
const labelText = sector.getTextContent() as graphic.Text;
const sector = this;
const labelLine = sector.getTextGuideLine();
const labelText = sector.getTextContent();
const seriesModel = data.hostModel;
const itemModel = data.getItemModel<PieDataItemOption>(idx);
......@@ -358,11 +324,9 @@ class PieView extends ChartView {
.add(function (idx) {
const piePiece = new PiePiece(data, idx);
// Default expansion animation
if (isFirstRender && animationType !== 'scale') {
piePiece.eachChild(function (child) {
child.stopAnimation(true);
});
}
// if (isFirstRender && animationType !== 'scale') {
// piePiece.stopAnimation(true);
// }
selectedMode && piePiece.on('click', onSectorClick);
......@@ -375,11 +339,9 @@ class PieView extends ChartView {
graphic.clearStates(piePiece);
if (!isFirstRender && animationTypeUpdate !== 'transition') {
piePiece.eachChild(function (child) {
child.stopAnimation(true);
});
}
// if (!isFirstRender && animationTypeUpdate !== 'transition') {
// piePiece.stopAnimation(true);
// }
piePiece.updateData(data, newIdx);
......
......@@ -128,11 +128,20 @@ class LabelManager {
const hostEl = label.__hostTarget;
const textConfig = hostEl.textConfig || {};
// TODO: If label is in other state.
const labelTransform = label.getComputedTransform();
const labelRect = label.getBoundingRect().plain();
BoundingRect.applyTransform(labelRect, labelRect, labelTransform);
dummyTransformable.setLocalTransform(labelTransform);
if (labelTransform) {
dummyTransformable.setLocalTransform(labelTransform);
}
else {
// Identity transform.
dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation =
dummyTransformable.originX = dummyTransformable.originY = 0;
dummyTransformable.scaleX = dummyTransformable.scaleY = 1;
}
const host = label.__hostTarget;
let hostRect;
......@@ -224,6 +233,8 @@ class LabelManager {
layoutOption = layoutOption || {};
if (hostEl) {
hostEl.setTextConfig({
// Force to set local false.
local: false,
// Ignore position and rotation config on the host el if x or y is changed.
position: (layoutOption.x != null || layoutOption.y != null)
? null : defaultLabelAttr.attachedPos,
......@@ -347,6 +358,10 @@ class LabelManager {
}
}
}
updateLabelGuidLine() {
}
}
......
......@@ -45,7 +45,7 @@ import IncrementalDisplayable from 'zrender/src/graphic/IncrementalDisplayable';
import * as subPixelOptimizeUtil from 'zrender/src/graphic/helper/subPixelOptimize';
import { Dictionary } from 'zrender/src/core/types';
import LRU from 'zrender/src/core/LRU';
import Displayable, { DisplayableProps } from 'zrender/src/graphic/Displayable';
import Displayable, { DisplayableProps, DisplayableState } from 'zrender/src/graphic/Displayable';
import { PatternObject } from 'zrender/src/graphic/Pattern';
import { GradientObject } from 'zrender/src/graphic/Gradient';
import Element, { ElementEvent, ElementTextConfig } from 'zrender/src/Element';
......@@ -377,23 +377,12 @@ function singleEnterEmphasis(el: Element) {
if (!el.states.emphasis) {
return;
}
const disp = el as Displayable;
const emphasisStyle = disp.states.emphasis.style;
const currentFill = disp.style && disp.style.fill;
const currentStroke = disp.style && disp.style.stroke;
el.useState('emphasis');
if (emphasisStyle && (currentFill || currentStroke)) {
if (!hasFillOrStroke(emphasisStyle.fill)) {
disp.style.fill = liftColor(currentFill);
}
if (!hasFillOrStroke(emphasisStyle.stroke)) {
disp.style.stroke = liftColor(currentStroke);
}
disp.z2 += Z2_EMPHASIS_LIFT;
}
el.useState('emphasis', true, {
duration: 300,
// TODO Configuration
easing: 'cubicOut'
});
const textContent = el.getTextContent();
if (textContent) {
......@@ -403,8 +392,10 @@ function singleEnterEmphasis(el: Element) {
}
function singleEnterNormal(el: Element) {
el.clearStates();
function singleLeaveEmphasis(el: Element) {
el.removeState('emphasis', {
duration: 300
});
(el as ExtendedElement).__highlighted = false;
}
......@@ -452,6 +443,38 @@ export function clearStates(el: Element) {
}
}
function elementStateProxy(this: Displayable, stateName: string): DisplayableState {
let state = this.states[stateName];
if (stateName === 'emphasis' && this.style) {
const currentFill = this.style.fill;
const currentStroke = this.style.stroke;
if (currentFill || currentStroke) {
state = state || {};
// Apply default color lift
let emphasisStyle = state.style || {};
let cloned = false;
if (!hasFillOrStroke(emphasisStyle.fill)) {
cloned = true;
// Not modify the original value.
state = extend({}, state);
emphasisStyle = extend({}, emphasisStyle);
emphasisStyle.fill = liftColor(currentFill);
}
if (!hasFillOrStroke(emphasisStyle.stroke)) {
if (!cloned) {
state = extend({}, state);
emphasisStyle = extend({}, emphasisStyle);
}
emphasisStyle.stroke = liftColor(currentStroke);
}
state.style = emphasisStyle;
}
}
return state;
}
/**
* Set hover style (namely "emphasis style") of element.
* @param el Should not be `zrender/graphic/Group`.
......@@ -462,6 +485,8 @@ export function enableElementHoverEmphasis(el: Displayable, hoverStl?: ZRStylePr
emphasisState.style = hoverStl;
}
el.stateProxy = elementStateProxy;
// FIXME
// It is not completely right to save "normal"/"emphasis" flag on elements.
// It probably should be saved on `data` of series. Consider the cases:
......@@ -469,7 +494,7 @@ export function enableElementHoverEmphasis(el: Displayable, hoverStl?: ZRStylePr
// again by dataZoom.
// (2) call `setOption` and replace elements totally when they are highlighted.
if ((el as ExtendedDisplayable).__highlighted) {
singleEnterNormal(el);
// singleLeaveEmphasis(el);
singleEnterEmphasis(el);
}
}
......@@ -485,7 +510,7 @@ export function leaveEmphasisWhenMouseOut(el: Element, e: ElementEvent) {
!shouldSilent(el, e)
// "emphasis" event highlight has higher priority than mouse highlight.
&& !(el as ExtendedElement).__highByOuter
&& traverseUpdateState((el as ExtendedElement), singleEnterNormal);
&& traverseUpdateState((el as ExtendedElement), singleLeaveEmphasis);
}
export function enterEmphasis(el: Element, highlightDigit?: number) {
......@@ -495,7 +520,7 @@ export function enterEmphasis(el: Element, highlightDigit?: number) {
export function leaveEmphasis(el: Element, highlightDigit?: number) {
!((el as ExtendedElement).__highByOuter &= ~(1 << (highlightDigit || 0)))
&& traverseUpdateState((el as ExtendedElement), singleEnterNormal);
&& traverseUpdateState((el as ExtendedElement), singleLeaveEmphasis);
}
function shouldSilent(el: Element, e: ElementEvent) {
......
......@@ -116,6 +116,7 @@ function boxLayout(
child.x = x;
child.y = y;
child.markRedraw();
orient === 'horizontal'
? (x = nextX + gap)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册