Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
x649585723
incubator-echarts
提交
fc4ca578
I
incubator-echarts
项目概览
x649585723
/
incubator-echarts
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
I
incubator-echarts
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
fc4ca578
编写于
4月 06, 2021
作者:
1
100pah
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: [geo svg] fix viewBox and add test cases.
上级
d8ebb5fd
变更
3
展开全部
隐藏空白更改
内联
并排
Showing
3 changed file
with
704 addition
and
74 deletion
+704
-74
src/coord/geo/GeoSVGResource.ts
src/coord/geo/GeoSVGResource.ts
+122
-71
test/geo-svg-demo.html
test/geo-svg-demo.html
+449
-3
test/geo-svg.html
test/geo-svg.html
+133
-0
未找到文件。
src/coord/geo/GeoSVGResource.ts
浏览文件 @
fc4ca578
...
...
@@ -108,7 +108,7 @@ export class GeoSVGResource implements GeoResource {
// in which there is no "view" info, so that it should better not to
// make references to graphic elements.
if
(
!
firstGraphic
)
{
firstGraphic
=
this
.
_firstGraphic
=
buildGraphic
(
this
.
_parsedXML
);
firstGraphic
=
this
.
_firstGraphic
=
this
.
_
buildGraphic
(
this
.
_parsedXML
);
this
.
_freedGraphics
.
push
(
firstGraphic
);
...
...
@@ -131,6 +131,126 @@ export class GeoSVGResource implements GeoResource {
};
}
private
_buildGraphic
(
svgXML
:
SVGElement
):
GeoSVGGraphicRecord
{
let
result
;
let
rootFromParse
;
try
{
result
=
svgXML
&&
parseSVG
(
svgXML
,
{
ignoreViewBox
:
true
,
ignoreRootClip
:
true
})
||
{};
rootFromParse
=
result
.
root
;
assert
(
rootFromParse
!=
null
);
}
catch
(
e
)
{
throw
new
Error
(
'
Invalid svg format
\n
'
+
e
.
message
);
}
// Note: we keep the covenant that the root has no transform. So always add an extra root.
const
root
=
new
Group
();
root
.
add
(
rootFromParse
);
(
root
as
GeoSVGGraphicRoot
).
isGeoSVGGraphicRoot
=
true
;
// [THE_RULE_OF_VIEWPORT_AND_VIEWBOX]
//
// Consider: `<svg width="..." height="..." viewBox="...">`
// - the `width/height` we call it `svgWidth/svgHeight` for short.
// - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say,
// "viewport boundingRect", or `boundingRect` for short.
// - `viewBox` defines the transform from the real content ot the viewport.
// `viewBox` has the same unit as the content of SVG.
// If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become
// different from the content of SVG. Otherwise, they are the same.
//
// If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be:
// 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']).
// 1. Make a transform from `viewBox` to `boundingRect`.
// Note: only suport `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve
// the aspect ratio.
// 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect'])
// (`Geo`/`View` will do this job).
// Note: this transform might not preserve aspect radio, which depending on how users specify
// viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio,
// but `geo.layoutCenter/layoutSize` will preserve aspect ratio).
//
// If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG
// layout look good.
//
// If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect
// of the SVG content and use them to make SVG layout look good.
const
svgWidth
=
result
.
width
;
const
svgHeight
=
result
.
height
;
const
viewBoxRect
=
result
.
viewBoxRect
;
let
boundingRect
=
this
.
_boundingRect
;
if
(
!
boundingRect
)
{
let
bRectX
;
let
bRectY
;
let
bRectWidth
;
let
bRectHeight
;
if
(
svgWidth
!=
null
)
{
bRectX
=
0
;
bRectWidth
=
svgWidth
;
}
else
if
(
viewBoxRect
)
{
bRectX
=
viewBoxRect
.
x
;
bRectWidth
=
viewBoxRect
.
width
;
}
if
(
svgHeight
!=
null
)
{
bRectY
=
0
;
bRectHeight
=
svgHeight
;
}
else
if
(
viewBoxRect
)
{
bRectY
=
viewBoxRect
.
y
;
bRectHeight
=
viewBoxRect
.
height
;
}
// If both viewBox and svgWidth/svgHeight not specified,
// we have to determine how to layout those element to make them look good.
if
(
bRectX
==
null
||
bRectY
==
null
)
{
const
calculatedBoundingRect
=
rootFromParse
.
getBoundingRect
();
if
(
bRectX
==
null
)
{
bRectX
=
calculatedBoundingRect
.
x
;
bRectWidth
=
calculatedBoundingRect
.
width
;
}
if
(
bRectY
==
null
)
{
bRectY
=
calculatedBoundingRect
.
y
;
bRectHeight
=
calculatedBoundingRect
.
height
;
}
}
boundingRect
=
this
.
_boundingRect
=
new
BoundingRect
(
bRectX
,
bRectY
,
bRectWidth
,
bRectHeight
);
}
if
(
viewBoxRect
)
{
const
viewBoxTransform
=
makeViewBoxTransform
(
viewBoxRect
,
boundingRect
);
// Only support `preserveAspectRatio 'xMidYMid'`
rootFromParse
.
scaleX
=
rootFromParse
.
scaleY
=
viewBoxTransform
.
scale
;
rootFromParse
.
x
=
viewBoxTransform
.
x
;
rootFromParse
.
y
=
viewBoxTransform
.
y
;
}
root
.
setClipPath
(
new
Rect
({
shape
:
boundingRect
.
plain
()
}));
const
named
=
[]
as
GeoSVGGraphicRecord
[
'
named
'
];
each
(
result
.
named
,
namedItem
=>
{
if
(
REGION_AVAILABLE_SVG_TAG_MAP
.
get
(
namedItem
.
svgNodeTagLower
)
!=
null
)
{
named
.
push
(
namedItem
);
setSilent
(
namedItem
.
el
);
}
});
return
{
root
,
boundingRect
,
named
};
}
/**
* Consider:
* (1) One graphic element can not be shared by different `geoView` running simultaneously.
...
...
@@ -151,7 +271,7 @@ export class GeoSVGResource implements GeoResource {
svgGraphic
=
this
.
_freedGraphics
.
pop
()
// use the first boundingRect to avoid duplicated boundingRect calculation.
||
buildGraphic
(
this
.
_parsedXML
,
this
.
_boundingRect
);
||
this
.
_buildGraphic
(
this
.
_parsedXML
);
usedRootMap
.
set
(
hostKey
,
svgGraphic
);
...
...
@@ -179,75 +299,6 @@ export class GeoSVGResource implements GeoResource {
}
function
buildGraphic
(
svgXML
:
SVGElement
,
// If input boundingRect, avoid boundingRect calculation,
// which might be time-consuming.
boundingRect
?:
BoundingRect
):
GeoSVGGraphicRecord
{
let
result
;
let
root
;
try
{
result
=
svgXML
&&
parseSVG
(
svgXML
,
{
ignoreViewBox
:
true
,
ignoreRootClip
:
true
})
||
{};
root
=
result
.
root
;
assert
(
root
!=
null
);
}
catch
(
e
)
{
throw
new
Error
(
'
Invalid svg format
\n
'
+
e
.
message
);
}
const
svgWidth
=
result
.
width
;
const
svgHeight
=
result
.
height
;
const
viewBoxRect
=
result
.
viewBoxRect
;
if
(
!
boundingRect
)
{
boundingRect
=
(
svgWidth
==
null
||
svgHeight
==
null
)
// If svg width / height not specified, calculate
// bounding rect as the width / height
?
root
.
getBoundingRect
()
:
new
BoundingRect
(
0
,
0
,
0
,
0
);
if
(
svgWidth
!=
null
)
{
boundingRect
.
width
=
svgWidth
;
}
if
(
svgHeight
!=
null
)
{
boundingRect
.
height
=
svgHeight
;
}
}
// Note: we keep the covenant that the root has no transform.
if
(
viewBoxRect
)
{
const
viewBoxTransform
=
makeViewBoxTransform
(
viewBoxRect
,
boundingRect
.
width
,
boundingRect
.
height
);
const
elRoot
=
root
;
root
=
new
Group
();
root
.
add
(
elRoot
);
elRoot
.
scaleX
=
elRoot
.
scaleY
=
viewBoxTransform
.
scale
;
elRoot
.
x
=
viewBoxTransform
.
x
;
elRoot
.
y
=
viewBoxTransform
.
y
;
}
root
.
setClipPath
(
new
Rect
({
shape
:
boundingRect
.
plain
()
}));
(
root
as
GeoSVGGraphicRoot
).
isGeoSVGGraphicRoot
=
true
;
const
named
=
[]
as
GeoSVGGraphicRecord
[
'
named
'
];
each
(
result
.
named
,
namedItem
=>
{
if
(
REGION_AVAILABLE_SVG_TAG_MAP
.
get
(
namedItem
.
svgNodeTagLower
)
!=
null
)
{
named
.
push
(
namedItem
);
setSilent
(
namedItem
.
el
);
}
});
return
{
root
,
boundingRect
,
named
};
}
function
setSilent
(
el
:
Element
):
void
{
// Only named element has silent: false, other elements should
// act as background and has no user interaction.
...
...
test/geo-svg-demo.html
浏览文件 @
fc4ca578
此差异已折叠。
点击以展开。
test/geo-svg.html
浏览文件 @
fc4ca578
...
...
@@ -40,6 +40,7 @@ under the License.
<div
id=
"main_simple_geo_json"
></div>
<div
id=
"main_simple_geo_svg"
></div>
<div
id=
"main_simple_geo_svg_coord"
></div>
<div
id=
"main_simple_geo_svg_viewBox_svgWH"
></div>
<div
id=
"main_geo_json_focus_blur"
></div>
<div
id=
"main_geo_svg_emphasis_select"
></div>
<div
id=
"main_pure_geo_svg"
></div>
...
...
@@ -298,6 +299,138 @@ under the License.
<script>
require
([
'
echarts
'
],
function
(
echarts
)
{
const
xmlHeader
=
'
<?xml version="1.0" encoding="utf-8"?>
'
;
const
svgTagCommonAttr
=
'
xmlns="http://www.w3.org/2000/svg" xmlns:ooo="http://xml.openoffice.org/svg/export" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" fill-rule="evenodd" xml:space="preserve"
'
;
// Both svgWdith svgHeight viewBox
const
svg1
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
viewBox="99500 99500 1000 1000"
'
,
'
width="5px" height="5px"
'
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="500" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
// Both svgWdith svgHeight viewBox and clip
const
svg2
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
viewBox="99500 99500 500 500"
'
,
'
width="5px" height="5px"
'
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="500" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
// Only svgWdith svgHeight
const
svg3
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
width="100000" height="100000"
'
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="100000" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
// Only viewBox
const
svg4
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
viewBox="99500 99500 500 500"
'
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="500" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
// Neither viewBox and svgWidth svgHeight
const
svg5
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="500" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
// Both viewBox but only svgHeight no svgWidth
const
svg6
=
[
xmlHeader
,
'
<svg
'
+
svgTagCommonAttr
,
'
viewBox="99500 99500 1000 1000"
'
,
'
height="500"
'
,
'
>
'
,
'
<circle cx="100000" cy="100000" r="500" fill="blue" stroke-linecap="square" stroke-linejoin="miter"/>
'
,
'
</svg>
'
].
join
(
'
'
);
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_1
'
,
{
svg
:
svg1
});
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_2
'
,
{
svg
:
svg2
});
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_3
'
,
{
svg
:
svg3
});
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_4
'
,
{
svg
:
svg4
});
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_5
'
,
{
svg
:
svg5
});
echarts
.
registerMap
(
'
testGeoSVG_viewBox_svgWH_6
'
,
{
svg
:
svg6
});
const
RECT_SIZE
=
50
;
const
Y_TOP
=
10
;
const
X_LEFT
=
10
;
const
GAP
=
20
;
var
_itemIndex
=
0
;
var
_graphicOptionArr
=
[];
var
_geoOptionArr
=
[];
function
makeItem
(
text
,
mapType
)
{
var
x
=
X_LEFT
+
(
RECT_SIZE
+
GAP
)
*
_itemIndex
++
;
_graphicOptionArr
.
push
({
type
:
'
rect
'
,
silent
:
true
,
shape
:
{
x
:
x
,
y
:
Y_TOP
,
width
:
RECT_SIZE
,
height
:
RECT_SIZE
},
style
:
{
stroke
:
'
red
'
,
lineWidth
:
1
,
fill
:
null
}
},
{
type
:
'
text
'
,
silent
:
true
,
style
:
{
text
:
text
,
x
:
x
+
RECT_SIZE
/
2
,
y
:
Y_TOP
+
RECT_SIZE
+
GAP
,
fill
:
'
#000
'
,
textAlign
:
'
center
'
,
textVerticalAlign
:
'
middle
'
}
});
_geoOptionArr
.
push
({
map
:
mapType
,
left
:
x
,
top
:
Y_TOP
,
width
:
RECT_SIZE
,
height
:
RECT_SIZE
,
itemStyle
:
{
color
:
'
blue
'
}
});
}
makeItem
(
'
circle
'
,
'
testGeoSVG_viewBox_svgWH_1
'
);
makeItem
(
'
1/4 sector
'
,
'
testGeoSVG_viewBox_svgWH_2
'
);
makeItem
(
'
1/4 sector
'
,
'
testGeoSVG_viewBox_svgWH_3
'
);
makeItem
(
'
1/4 sector
'
,
'
testGeoSVG_viewBox_svgWH_4
'
);
makeItem
(
'
circle
'
,
'
testGeoSVG_viewBox_svgWH_5
'
);
makeItem
(
'
thin ellipse
'
,
'
testGeoSVG_viewBox_svgWH_6
'
);
option
=
{
animation
:
false
,
tooltip
:
{
},
graphic
:
_graphicOptionArr
,
geo
:
_geoOptionArr
};
var
chart
=
testHelper
.
create
(
echarts
,
'
main_simple_geo_svg_viewBox_svgWH
'
,
{
title
:
[
'
viewBox test:
'
,
'
Check each shape should be inside each red rect.
'
,
'
Check each shape be correspondence with the text.
'
,
],
option
:
option
,
height
:
100
});
});
</script>
<script>
require
([
'
echarts
'
/*, 'map/js/china' */
],
function
(
echarts
)
{
const
testGeoJson1
=
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录