首页 > 解决方案 > 如何在 SVG JS 中加载自定义 svg

问题描述

我正在拆分一个 pong 演示 ( https://css-tricks.com/pong-svg-js/ ) 来了解 Javascript、SVG 和 SVG.js。

我的版本将绘制一个在窗口中来回弹跳的球。使用“var ball = draw.circle(ballsize)”它可以正常工作,但是当我尝试替换自定义 SVG 时,它会失败。

我该如何纠正这个问题以绘制我的自定义 svg 来代替球?

<html>
<body translate="no" >
<div id="svg-data" hidden>
<svg id="svg2" width="720" height="720" xmlns="http://www.w3.org/2000/svg">
 <g>
  <title>background</title>
  <rect fill="none" id="canvas_background" height="602" width="802" y="-1" x="-1"/>
 </g>
 <g>
  <title>Layer 1</title>
  <g id="layer1">
   <path d="m339.03,161.54001c166.28,1.498 323.57,64.413 335.55,203.73c8.98,101.9 -125.84,194.73 -292.11,194.73c-167.78,-1.5 -323.57,-62.91 -337.05,-202.18c-8.987,-103.45 127.24,-196.28 293.61,-196.28zm40.352,364.01c71.903,0 118.34,-68.954 110.85,-124.33c-16.478,-109.35 -53.928,-197.78 -152.79,-197.78c-71.903,0 -113.85,70.405 -104.86,125.83c16.571,109.4 48.029,196.28 146.8,196.28z" id="path270"/>
  </g>
 </g>
</svg>
</div>
  <div id="pong"></div>

<script src='https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.7.1/svg.min.js'></script>
  
<script id="rendered-js" >
// define document width and height
var width = 450,height = 300;

// create SVG document and set its size
var draw = SVG('pong').size(width, height);
draw.viewbox(0, 0, 450, 300);


// draw background
var background = draw.rect(width, height).fill('#dde3e1');


// define ball size
var ballSize = 10;

// original image - create ball
//var ball = draw.circle(ballSize);

// custom image - fails
var ball = draw.svg(document.getElementById('svg2').innerHTML);
ball.size(30,30);
ball.center(width / 2, height / 2).fill('#7f7f7f');

var ballDirection = -1;
var ballSpeed = 100;

function update(dt) {
    ball.dmove( ballDirection * ballSpeed * dt, 0);
    
    if (ball.cx() < 0 || ball.cx() > width) {
        if (ball.cx() < 0) { 
            ball.cx(0); 
        }
        else if (ball.cx() > width) { 
            ball.cx(width); 
        }
        ballDirection *= -1;
    }
}

var lastTime, animFrame;

function callback(ms) {
    cancelAnimationFrame(animFrame);
    
    if (lastTime) {
        update((ms - lastTime) / 1000);
    }
    
    lastTime = ms;
    animFrame = requestAnimationFrame(callback);
}

callback();

</script>
  
</body>
</html>

标签: javascriptsvg

解决方案


假设您有一个包含网球图形的文件。我选择了该文件的最小尺寸,您想要使用的标记内容可能会更大:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
  <circle fill="#77B255" cx="18" cy="18" r="18"/>
  <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/>
  <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/>
  <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/>
</svg>

SVG.js 将能够在 SVG<image>标签中引用该文件:

var ball = draw.image('/path/to/Twemoji12 1f3be.svg').size(ballsize, ballsize)

但这有一个缺点:球是从一个单独的文件加载的,你需要一个需要时间来填充的请求(就像每个图像文件一样)。

但是球只是你可以写入主文件的东西。这就是你的 grafic 标记的大小会产生影响的地方。

变体 1:分别绘制每个标签

这就是 SVG.js 真正要做的事情。

// draw a wrapper element, here you would use a nested `<svg>` element
var ball = draw.svg().size(ballsize, ballsize).viewbox(0 0 36 3)
// draw the circle
ball.circle(18).cx(18).cy(18).fill('#77B255')
// and so on for every tag and every attribute

坦率地说:这只是比你可能想做的更多的工作。

变体 2:从字符串构造元素

var ball = SVG(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36">
  <circle fill="#77B255" cx="18" cy="18" r="18"/>
  <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/>
  <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/>
  <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/>
</svg>`)
draw.add(ball).size(ballsize, ballsize)

好一点,但至少我不喜欢在同一个文件中混合 Javascript 和标记。我会去

变体 3:绕过 SVG.js,直接在 HTML 标记中编写标记

我在这里给你一个很高的学习曲线,但最终你想摆脱它,不是吗?

SVG 包含自己的模板机制,其中包含一个名为<symbol>. 这是您的初始标记的外观:

<div id="pong">
  <svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
    <symbol id="ball" viewBox="0 0 36 36">
      <circle fill="#77B255" cx="18" cy="18" r="18"/>
      <path fill="#A6D388" d="M26 18c0 6.048 2.792 10.221 5.802 11.546C34.42 26.42 36 22.396 36 18c0-4.396-1.58-8.42-4.198-11.546C28.792 7.779 26 11.952 26 18z"/>
      <path fill="#FFF" d="M27 18c0-6.048 1.792-10.221 4.802-11.546-.445-.531-.926-1.028-1.428-1.504C27.406 6.605 25 10.578 25 18c0 7.421 2.406 11.395 5.374 13.05.502-.476.984-.973 1.428-1.504C28.792 28.221 27 24.048 27 18z"/>
      <path fill="#A6D388" d="M10 18c0-6.048-2.792-10.22-5.802-11.546C1.58 9.58 0 13.604 0 18c0 4.396 1.58 8.42 4.198 11.546C7.208 28.22 10 24.048 10 18z"/><path fill="#FFF" d="M4.198 6.454C7.208 7.78 9 11.952 9 18c0 6.048-1.792 10.22-4.802 11.546.445.531.926 1.027 1.428 1.504C8.593 29.395 11 25.421 11 18c0-7.421-2.406-11.395-5.374-13.049-.502.476-.984.972-1.428 1.503z"/>
    </symbol>
  </svg>
</div>

我在这里做了什么?

  • 我在你的 wrapper 中复制了文件内容<div>
  • 然后我插入了一个<symbol>元素,以便它包裹图形元素。
  • 元素的<svg>大小为零,因为它最初只包含模板(我正在简化。)
  • viewBox属性被移动到<symbol>并添加了一个唯一的 id。

现在,您的脚本可以像以前一样工作了。线

var draw = SVG('pong').size(width, height)

只会<svg>在第一个不可见的元素之后插入第二个元素。但你也可以拿起第一个并在那里画。初始零大小被覆盖。

var draw = SVG('#pong svg').size(width, height)

在这两种变体中,球都以参考的形式使用:

var ball = draw.use('ball').size(ballsize, ballsize)

推荐阅读