svg : defs 标签
本帖最后由 马黑黑 于 2023-11-13 23:24 编辑 <br /><br /><style>.papa > p { margin: 10px 0; }
.mama { margin-left: 40px; position: relative; }
.hCode, .hLineNum { padding: 10px; width: calc(100% - 40px); font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; background: #f9f9f9; box-sizing: border-box; overflow-x: auto; tab-size: 4; position: absolute; }
.hCode { left: 40px; margin-left: -40px; padding-left: 45px; }
.hLineNum { width: 40px; background: #ccc; border-right: 1px solid #ccc; text-align: right; pointer-events: none; }
.stage { display: grid; place-items: center; }
.hidden { display: none; }
</style>
<div class="papa">
<p>defs用于定义页面中需要重复使用的图形元素,包括实体图形(如 rect、circle、polygon、path等)和一些修饰机制(如滤镜、渐变等)。它和g标签同类,但各自功用不同:g标签用于分组,实时呈现其内定义的实体;defs标签内的实体则是隐形的,仅在调用实体id后方能复现,可以多次使用;defs标签也极其单纯,它是它自身,基本没有别的东东,包括id都没有。</p>
<p>应该注意的是,defs内定义的东东不同,引用对象和方式也不同。比如滤镜,调用滤镜的实体元素要通过 filter 引用其内的滤镜id,且现在的浏览器,滤镜要不要defs都可以;而渐变,调用者使用的是fill;实体元素呢,通过 <use> 标签的 xlink: href 或 href 属性调用。</p>
<p>以下示例,defs 标签包裹了两个实体元素:一个polygon绘制的菱形和一个circle绘制的小圆点。然后,两个 use 标签通过 xlink:href="#id标识" 调用实体元素,呈现出defs定义的图形元素。use 标签有自己的x和y属性,用于设置呈现内容左上角位于svg画布的xy坐标(具体还要参照defs内实体元素的位置),含义相当于CSS的left和top属性。</p>
<div class="mama">
<pre class="hCode"><svg width="200" height="200" style="border: 1px solid gray;">
<defs>
<polygon id="poly" points="100 50, 50 100, 0 50, 50 0" fill="none" stroke="red" />
<circle id="circ" cx="50" cy="50" r="5" fill="green" />
</defs>
<use xlink:href="#poly" x="50" y="50" />
<use xlink:href="#circ" x="20" y="20" />
</svg></pre>
<pre class="hLineNum"></pre>
</div>
<p><button class="btnok" type="button" value="运行代码">运行代码</button></p>
<div class="stage"></div>
<p>小结:defs非常简单,应注意的是,defs内的内容,如果不被调用,它永远不会显示出来。要显示它定义的图形,需要将其实例化,比如 filter="url(#id)"、fill="url(#id)"、use 标签的 xlink:href="#id" 或 href="#id" 等方式引用其内的图形或修饰类元素。</p>
</div>
<script>
let btns = document.querySelectorAll('.btnok'),
stages = document.querySelectorAll('.stage'),
hCodes = document.querySelectorAll('.hCode'),
hLineNums = document.querySelectorAll('.hLineNum'),
mamas = document.querySelectorAll('.mama');
hCodes.forEach((item,key) => {
let lines = hCodes.innerText.trim().split('\n').length;
let str = '';
for(let i = 0; i < lines; i ++) {
str += i + 1 + '\n';
}
hLineNums.innerText = str;
mamas.style.cssText += `height: ${hCodes.offsetHeight + 20}px`;
if(!btns) return;
btns.onclick = () => {
let val = btns.value;
val === '运行代码' ? codeRun(hCodes.innerText, stages) : codeRun('',stages);
btns.value = btns.innerText = val === '运行代码' ? '关闭运行' : '运行代码';
};
});
let codeRun = (str,ele) => {
let reg = /(<script(.*?)>)(.|\n)*?(<\/script>)/g;
let js_str, html_str;
if(str.match(reg) !== null) {
js_str = str.match(reg);
html_str = str.replace(js_str, '').trim();
js_str = js_str.replace(/<[\/]{0,1}script[^>]*>/g,'').trim();
} else {
js_str = '';
html_str = str.trim();
}
ele.innerHTML = html_str;
let myfunc = new Function(js_str);
myfunc();
};
</script>
思考:
一楼的演示效果,第二个 use 标签方位属性 x="20" y="20",按理应该在 svg 左上角不远处,可结果有很大的偏离。为什么? “滤镜要不要defs都可以”这个好像在讲滤镜的时候也讲过的。
这个帖子好,不但讲解了defs,顺便也把各种引用方式都复习了一遍{:4_199:} 马黑黑 发表于 2023-11-13 23:21
思考:
一楼的演示效果,第二个 use 标签方位属性 x="20" y="20",按理应该在 svg 左上角不远处,可结果 ...
因为它的cx="50" cy="50"
而两个20是在这两个50为参照的情况下移动的{:4_173:} 也就是说,defs引用的时候,是以图形原本位置为参照移动的,而不是svg的位置参照。是这样理解吧? 红影 发表于 2023-11-14 09:26
因为它的cx="50" cy="50"
而两个20是在这两个50为参照的情况下移动的
理解正确。defs中的实体元素自身的定位会参与到use元素的定位,不过都已svg坐标系为参照 红影 发表于 2023-11-14 09:30
也就是说,defs引用的时候,是以图形原本位置为参照移动的,而不是svg的位置参照。是这样理解吧?
是的 红影 发表于 2023-11-14 09:21
“滤镜要不要defs都可以”这个好像在讲滤镜的时候也讲过的。
这个帖子好,不但讲解了defs,顺便也把各种引 ...
没错 马黑黑 发表于 2023-11-14 12:19
理解正确。defs中的实体元素自身的定位会参与到use元素的定位,不过都已svg坐标系为参照
嗯嗯,你的提问很好,可以让人进一步思索{:4_187:} 马黑黑 发表于 2023-11-14 12:20
是的
那么use xlink:href="#circ" x="20" y="20"
的xy可否取-20? 也是在svg范围内呢。 马黑黑 发表于 2023-11-14 12:20
没错
我自己再复习一下:
对于调用,调用滤镜的实体元素要通过 filter 引用其内的滤镜id,;而渐变,调用者使用的是fill;实体元素呢,通过 <use> 标签的 xlink: href 或 href 属性调用。 红影 发表于 2023-11-14 20:08
嗯嗯,你的提问很好,可以让人进一步思索
这是必须的 红影 发表于 2023-11-14 20:10
那么use xlink:href="#circ" x="20" y="20"
的xy可否取-20? 也是在svg范围内呢。
这个可以自己试一试 马黑黑 发表于 2023-11-14 21:32
这是必须的
否则看过就过了{:4_173:} 马黑黑 发表于 2023-11-14 21:32
这个可以自己试一试
试过了,是可以的呢{:4_173:} 红影 发表于 2023-11-14 22:31
试过了,是可以的呢
有一些属性值不允许负值。哪一些可以哪一些不可以,其实不复杂,想想就能知道 马黑黑 发表于 2023-11-15 00:01
有一些属性值不允许负值。哪一些可以哪一些不可以,其实不复杂,想想就能知道
嗯嗯,反正定位上肯定是可以的{:4_173:} 红影 发表于 2023-11-15 14:35
嗯嗯,反正定位上肯定是可以的
一般这类东东会遵循来自生活经验的规律 马黑黑 发表于 2023-11-15 18:10
一般这类东东会遵循来自生活经验的规律
哦,那倒挺好的,更容易理解了。 红影 发表于 2023-11-15 21:54
哦,那倒挺好的,更容易理解了。
都是这么设计来着