简体   繁体   中英

How to draw sine waves with SVG (+JS)?

What would be the simplest solution to draw sine waves in SVG? I guess sine waves should be repeated in a simple loop with JavaScript... :)

<svg>
  <line x1="0" y1="250" x2="500" y2="250"
        style="stroke:black;stroke-width:1"/>
  <line x1="250" y1="0" x2="250" y2="500"
        style="stroke:black;stroke-width:1"/>
</svg>

An alternative to straight-line approximations would be a Bézier approximation. A pretty good approximation of the first quarter of one period is a cubic Bézier curve with the following control points:

 0   0
1/2 1/2
 1   1
π/2  1

Edit: Even more exact approximations are possible with the following control points:

0                    0
0.512286623256592433 0.512286623256592433
1.002313685767898599 1
1.570796326794896619 1

(See NominalAnimal's explanations in the comments)

Demo comparing line elements (gray) and "good" Bézier (red) and "better" Bézier (green).

An approximation that exactly interpolates the slope and curvature in the spline's endpoints is

       0                0 
(6−(3/2π−3)²)/6  (6−(3/2π−3)²)/6
       1                1
      π/2               1

( See derivation )

The following will add a one-cycle sine wave to your SVG graph:

var XMAX = 500;
var YMAX = 500;

// Create path instructions
var path = [];
for (var x = 0; x <= XMAX; x++) {
    var angle = (x / XMAX) * Math.PI * 2;  // angle = 0 -> 2π
    var y = Math.sin(angle) * (YMAX / 2) + (YMAX / 2);
    // M = move to, L = line to
    path.push((x == 0 ? 'M' : 'L') + x + ',' + y);
}

// Create PATH element
var pathEl = document.createElementNS("http://www.w3.org/2000/svg", "path");
pathEl.setAttribute('d', path.join(' ') );
pathEl.style.stroke = 'blue';
pathEl.style.fill = 'none';

// Add it to svg element
document.querySelector('svg').appendChild(pathEl);

​    ​

Here's the jsfiddle you can run .

This uses a PATH element made up of 'lineto' (straight line) commands. This works because, not surprisingly, it contains many (500) small line segments. You could simplify the path to have fewer points by using bezier curves to draw the segments, but this complicates the code. And you asked for simple. :)

Here is a proof of concept that adds multiple line elements to the SVG element:

 var svg = document.getElementById('sine_wave').children[0]; var origin = { //origin of axes x: 100, y: 100 }; var amplitude = 10; // wave amplitude var rarity = 1; // point spacing var freq = 0.1; // angular frequency var phase = 0; // phase angle for (var i = -100; i < 1000; i++) { var line = document.createElementNS("http://www.w3.org/2000/svg", "line"); line.setAttribute('x1', (i - 1) * rarity + origin.x); line.setAttribute('y1', Math.sin(freq*(i - 1 + phase)) * amplitude + origin.y); line.setAttribute('x2', i * rarity + origin.x); line.setAttribute('y2', Math.sin(freq*(i + phase)) * amplitude + origin.y); line.setAttribute('style', "stroke:black;stroke-width:1"); svg.appendChild(line); } 
 html, body, div{ height:100%; } 
 <div id="sine_wave"> <svg width="1000" height="1000"> <line x1="100" y1="0" x2="100" y2="200" style="stroke:black;stroke-width:1"/> <line x1="0" y1="100" x2="1000" y2="100" style="stroke:black;stroke-width:1"/> </svg> </div> 

In case it is useful to anybody: Here is a one-liner SVG that closely approximates half of a sine wave using a cubic bezier approximation.

<svg width="100px" height="100px" viewBox="0 0 100 100">
    <path stroke="#000000" fill="none" d="M0,0 C36.42,0,63.58,100,100,100" />
</svg>

I fitted the parameter 36.42 by minimizing the sum-squared (l2) distance between the bezier curve and the true cosine curve. https://octave-online.net/bucket~AN33qHTHk7eARgoSe7xpYg

My answer is based in part on How to approximate a half-cosine curve with bezier paths in SVG?

在X轴上循环,并且对于每次迭代,使用当前X值上的正弦函数计算Y位置。

For use in illustrations, this approximation works well.

<path d="M0,10 q5,20,10,0 t 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10 0 10,0" stroke="black" fill="none"/>

Here's a CDMA illustration where I've used cubic splines for illustrating CDMA concepts. First define these functions:

<script>
function go(x,y) {return(`M ${x},${y}`)}
function to(y) {return(`c 5 0 5 ${y} 10 ${y}`)}
function dn(y=10) {return to(y)}
function up(y=10) {return to(-y)}
function path(d,color='black') {return `<path d="${d}" stroke=${color} fill="none"/>`}
function svg(t) {return `<svg>${t}</svg>`}
function bits(n) {
  let s='', n0=(n>>1)
  for (m=0x80;m;m>>=1) s+= up(10* (!(m&n0)-!(m&n))  )
  return s;
}
function plot(a) {
  let s='', y0=0
  for (let y of a) {
    s += up(y-y0); y0=y
  }
  return s
}
function add(a) {
  let s=''
  if typeof y0 == 'undefined' var y0=0
  for (m=0x80;m;m>>=1) {
    let y=0; for (let e of a) y+= 5-10*!(e&m)
    s += up(y-y0); y0=y   
  }
  return s
}
</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM