跳至主要內容

01.svg的学习之路

pinia原创大约 10 分钟SVGsvg

初识SVG

MDN_SVGSVG:可缩放矢量图形 | MDN (mozilla.org)open in new window

SVG是一种基于XML语法的图像格式,全程是可缩放矢量图(Scalable Vector Graphics )。其他图像格式都是基于像素进行处理的,而SVG是属于对图形的形状的描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。

SVG 的优势

由于 SVG 图像是矢量图像,可以无限缩放,而且在图像质量下降方面没有任何问题。为什么会这样呢?因为 SVG 图像是使用 XML 标记构建的,浏览器通过绘制每个点和线来打印它们,而不是用预定义的像素填充某些空间。这确保 SVG 图像可以适应不同的屏幕大小和分辨率,即使是那些尚未发明的。

由于是在 XML 中定义的,SVG 图像比 JPG 或 PNG 图像更灵活,而且我们可以使用 CSS 和 JavaScript 与它们进行交互。SVG 图像设置可以包含 CSS 和 JavaScript。

SVG 可以渲染比其他格式小得多的矢量风格图像,主要用于标识和插图。另一个巨大的用例是图标。曾经是图标字体域,比如 FontAwesome,现在的设计师更喜欢使用 SVG 图像,因为它更小,并且允许使用多色图标。

SVG 在动画方面很简单,这是一个非常酷的话题。

SVG 提供了一些图像编辑效果,比如屏蔽和剪裁、应用过滤器等等。

SVG 只是文本,因此可以使用 GZip 对其进行有效压缩。

SVG文件使用的方式

  • 网页(直接插入网页,称为DOM的一部分,然后使用js和css进行操作)
  • 使用svg的五种方式:
    • 直接用img标签引入,不可以操作svg的内容
    • 用iframe标签引入,
    • 直接使用svg标签
    • object也可以使用
    • 用embed也可以使用
SVG
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>svg</title>
</head>
<body>
<!--
/svg的使用方式:
1.直接用img标签引入,不可以操作svg的内容
2.用iframe标签引入,
3.直接使用,
4.object也可以使用
5.用embed也可以使用
但是不能用background-image
-->
<svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1"
     xmlns="http://www.w3.org/2000/svg">
    <path d="M219.424 18.304h530.272l201.152 219.424v768H219.424V18.304z" fill="#FFFFFF"/>
    <path d="M733.696 253.728V50.304H251.424v923.424h667.424v-720h-185.152z m217.152-16v768H219.424V18.304h530.272l201.152 219.424z m-58.08-16l-127.04-138.624v138.624h127.04z"
          fill="#465F78"/>
    <path d="M64 288a32 32 0 0 1 32-32h448a32 32 0 0 1 32 32v448a32 32 0 0 1-32 32H96a32 32 0 0 1-32-32V288zM640 416h224v37.344h-224V416zM640 509.344h224v37.312h-224v-37.312zM640 602.656h224V640h-224v-37.344z"
          fill="#FA7553"/>
    <path d="M168.416 672H105.152l96.384-279.264h76.096L373.92 672H310.656L240.64 456.544h-2.176L168.416 672z m35.712-109.76h70.88l14.944 46.08h-100.8l14.976-46.08zM417.824 671.616v-174.528h48.416v174.528h-48.416z m24.32-197.024a26.304 26.304 0 0 1-18.56-7.168 23.232 23.232 0 0 1-7.584-17.376 22.72 22.72 0 0 1 7.616-17.184 26.08 26.08 0 0 1 18.528-7.264c7.2 0 13.312 2.432 18.4 7.264 5.152 4.8 7.744 10.496 7.744 17.184a23.04 23.04 0 0 1-7.744 17.376 25.92 25.92 0 0 1-18.4 7.168z"
          fill="#FFFFFF"/>
</svg>
<img src="./assets/ai.svg" alt="svg">
<iframe src="./assets/ai.svg" scrolling="no" width="450" height="200" style="boder:none;"></iframe>
<object data="./assets/ai.svg" type="image/svg+xml"></object>
<embed src="./assets/ai.svg" type="image/svg+xml">
</body>
</html>

svg语法

1.1 circle 圆

<svg width="100" height="100" viewBox="50,50,50,50">
<!--svg标签 默认大小300px*150px viewBox="50,50,50,50" 代表坐标系 50,50起点 50,50宽高-->
    <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
    <!--圆形 cx,cy圆心坐标 r半径 stroke边框颜色 stroke-width边框宽度 fill填充颜色 样式还可以通过css样式进行设置-->
</svg>

viewBox属性的四个值风别代表横纵坐标,宽高

注意

视口必须适配所在的空间,上面代码中视口是50,50,而svg图像的大小为100px*100px,所以视口会放大去适配svg图像的大小,即放大了四倍。

如果不指定宽高,只指定viewBox属性,则相当于只给定svg图像的长宽比,这时,svg的图像默认大小等于html元素的大小。

1.2 line 线段

线段
<svg width="400" height="400">
    <line x1="50" y1="50" x2="350" y2="350" stroke="black" stroke-width="5"></line>//直线 x1 y1 起点坐标 x2 y2 终点坐标
</svg>

1.3 polyline折线

折线
<svg width="400" height="400">
    <polyline points="10,10 100,10 100,100 10,100 10,10" style="fill:none;stroke:black;stroke-width:3"/>
    <!--
    折线 points="x1,y1 x2,y2 x3,y3 x4,y4 x5,y5"
    style="fill:none;stroke:black;stroke-width:3"
    -->
</svg>

1.4 rect 矩形

矩形
<svg width="400" height="400">
    <rect x="100" y="100" width="100" height="100" fill="red" stroke="#9370DB" stroke-width="10px"/>
</svg>

1.5 ellipse 椭圆

椭圆
<svg width="400" height="400">
    <ellipse cx="200" cy="200" rx="200" ry="100" fill="red" stroke="blue" stroke-width="5"/>
</svg>

1.6 polygon 多边形

::: normal-demo多边形

<svg width="400" height="400">
    <polygon points="200,10 250,190 160,210" style="fill:lime;stroke:purple;stroke-width:1"/>
</svg>

:::

1.7 path 绘制路径

  1. M:起点
  2. L:直线
  3. H:水平线
  4. V:垂直线
  5. Q:二次贝塞尔曲线
  6. T:光滑二次贝塞尔曲线
  7. C:三次贝塞尔曲线
  8. S:光滑三次贝塞尔曲线
  9. Z:闭合
绘制路径
<svg width="400" height="400">
    <path d="M 50,50 L 100,50 L 100,100 L 50,100 Z" fill="skyblue" stroke="#9370DB" stroke-width="5"></path>//M起点,L直线,Z闭合
</svg>

注意

命令的字母大小写,表示的意义是不同的。大写字母表示绝对定位,小写字母表示相对定位。绝对定位是相对于屏幕坐标原点的位置,相对定位是相对于上一个绘制点的位置。

1.8 text 绘制文本

绘制文本
<svg width="400" height="400">
    <text x="180" y="100" fill="none" font-size="40" font-weight="900" stroke="skyblue">hello,svg</text>
</svg>

1.9 use复制一个形状

use复制一个形状
<svg width="400" height="400" >
    <path id="scene-container" d="M 50,50 L 100,50 L 100,100 L 50,100 Z" fill="skyblue" stroke="#9370DB" stroke-width="5"></path>
    <use href="#scene-container" x="150" y="150"></use><!--    use标签可以复用path标签,注意id的使用需要加#-->
</svg>

1.10 g标签形成一个组,方便后续的使用

g形成一个组
<svg width="800" height="400">
    <!--
    g:定义一个组
    use:使用一个组
    -->
    <g id="group">
        <rect x="0" y="0" width="100" height="100" fill="red"/>
        <rect x="100" y="0" width="100" height="100" fill="green"/>
        <rect x="200" y="0" width="100" height="100" fill="blue"/>
        <rect x="0" y="100" width="100" height="100" fill="yellow"/>
        <rect x="100" y="100" width="100" height="100" fill="purple"/>
        <rect x="200" y="100" width="100" height="100" fill="orange"/>
    </g>
    <use href="#group" x="310" y="0"></use>
    <use href="#group" x="0" y="210"></use>
    <use href="#group" x="310" y="210"></use>
</svg>

1.11 defs 用于自定义形状,代码不会显示,仅供调用

defs定义一个形状
<svg width="800" height="400">
    <!--
    defs:声明一个自定义形状
    g:定义一个组
    use:使用一个组
    -->
    <defs>
        <g id="group">
            <rect x="0" y="0" width="100" height="100" fill="red"/>
            <rect x="100" y="0" width="100" height="100" fill="green"/>
            <rect x="200" y="0" width="100" height="100" fill="blue"/>
            <rect x="0" y="100" width="100" height="100" fill="yellow"/>
            <rect x="100" y="100" width="100" height="100" fill="purple"/>
            <rect x="200" y="100" width="100" height="100" fill="orange"/>
        </g>
    </defs>
    <use href="#group" x="310" y="0"></use>
    <use href="#group" x="0" y="210"></use>
    <use href="#group" x="310" y="210"></use>
</svg>

1.12 pattern 用于自定义一个形状,平铺一个区域

pattern标签
<svg width="800" height="500">
    <!--
    defs:声明一个自定义形状
    pattern:声明一个图案
    rect:矩形 fill="url(#pattern)" 填充图案
    -->
    <defs>
        <pattern id="pattern" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
            <rect x="0" y="0" width="100" height="100" fill="red" stroke="black" stroke-width="4"></rect>
            <circle cx="50" cy="50" r="40" fill="yellow"></circle>
        </pattern>
    </defs>
    <rect x="0" y="0" width="800" height="500" fill="url(#pattern)"></rect>
</svg>

1.13 image 用于插入图片文件

<svg width="800" height="500">
    <!--
    image标签的xlink:href属性指向图片的路径
    -->
    <image xlink:href="./assets/123.jpg" x="0" y="0" width="100" height="100" />
</svg>

注意

图片放svg矢量图里会真

1.14 animate 产生动画效果

animate 产生动画效果
<svg width="600" height="400">
    <!--
    rect 用于定义矩形 动画必须在标签内部定义
    animate 用于定义动画 有两个属性 attributeName 和 to 用于定义动画的属性和属性值 dur 用于定义动画的持续时间 repeatCount 用于定义动画的重复次数
    所有动画会合并到一个动画中
    -->
    <rect x="10" y="10" width="100" height="50" fill="red" stroke="black" stroke-width="1">
        <animate attributeName="x" from="0" to="600" dur="5s" repeatCount="indefinite"></animate>
        <animate attributeName="y" from="0" to="400" dur="5s" repeatCount="indefinite"></animate>
        <animate attributeName="fill" from="black" to="skyblue" dur="5s" repeatCount="indefinite"></animate>
    </rect>/
</svg>

1.15 animateTransform 对css里的transform不生效,需要用这个属性进行变形

animateTransform css不生效时使用
<svg width="600" height="400">
    <!--
    rect 用于定义矩形 动画必须在标签内部定义
    animateTransform 用于定义动画 attributeName="transform" 用于定义动画类型  attributeType="XML" 用于定义动画类型  type="rotate" 用于定义动画类型  from="0 30 30" 用于定义动画类型  to="360 60 60" 用于定义动画类型  dur="5s" 用于定义动画类型  repeatCount="indefinite" 用于定义动画类型
    form 用于定义动画的起始值(第一个值为角度,第二个值为x轴坐标,第三个值为y轴坐标)
    -->
    <rect x="100" y="100" width="100" height="50" fill="red" stroke="black" stroke-width="1">
        <animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 0 200" to="360 600 200" dur="5s" repeatCount="indefinite"></animateTransform>
    </rect>/
</svg>

JavaScript操作SVG

js操作svg变化
<svg width="400" height="400">
    <rect id="rect" x="5" y="5" width="100" height="100" fill="red" stroke="black" stroke-width="5"/>
</svg>
<button id="scaleBtn">放大矩形</button>
let btn = document.getElementById('scaleBtn');
btn.onclick = () => {
    let rect = document.getElementById('rect');
    //获取宽高
    let width = rect.getAttribute('width');
    let height = rect.getAttribute('height');
    //设置宽高
    rect.setAttribute('width', width * 1.2);
    rect.setAttribute('height', height * 1.2);
    //设置颜色
    rect.style.fill = 'skyblue'
    //设置位置
    let position = 5
    //设置定时器
    let timer = setInterval(() => {
        if (position >= 400) {
            position = 5
            clearInterval(timer)
        }
        position += 5
        rect.setAttribute('x', position)
    }, 100)
}

经典案例

1.SVG环形进度条

环形进度条
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg">
    <circle cx="100" cy="100" r="90" stroke="grey" stroke-width="20" fill="none" stroke-linecap="round"></circle>
    <circle id="process" transform="rotate(-90,100,100)" cx="100" cy="100" r="90" stroke="red" stroke-width="20"
            fill="none" stroke-linecap="round" stroke-dasharray='0,1000'></circle>
    <text x="100" y="100" text-anchor="middle" dy="10" font-size="40" fill="skyblue">0%</text>
</svg>
const process = document.getElementById('process');
const text = document.getElementsByTagName('text')[0];
const rotateCircle = (percent) => {
    //计算圆的周长
    let circleLength = Math.floor(Math.PI * 2 * parseFloat(process.getAttribute('r')))
   //计算进度条的长度
    let value = Math.floor(circleLength * percent / 100)
    //计算进度条的颜色
    let red = 255 + parseInt((0 - 255) / 100 * percent)
    let green = parseInt((191 - 0) / 100 * percent)
    let blue = 0 + parseInt((255 - 0) / 100 * percent)
    //设置stroke-dasharray的的路径和颜色
    process.setAttribute('stroke-dasharray', value + ',' + circleLength)
    process.setAttribute('stroke', 'rgb(' + red + ',' + green + ',' + blue + ')')
    text.innerHTML = percent + '%'
    text.setAttribute('fill', 'rgb(' + red + ',' + green + ',' + blue + ')')
}
let percent = 0;
let timer = setInterval(() => {
    percent++;
    if (percent >= 100) {
        clearInterval(timer)
    }
    rotateCircle(percent)
}, 30)

2.SVG绘制条形统计图

svg绘制条形统计图
<!--
创建svg元素
创建坐标系,坐标文字
依据数据绘制矩形
-->
<svg width="1000" height="700">
    <g id="coordinate">
        <line x1="50" y1="600" x2="950" y2="600" stroke="#999" stroke-width="3"></line>
        <path d="M 950,590 L 970,600 L 950,610 " fill="#999"></path>
        <text x="910" y="620" fill="#999">星期</text>
        <line x1="100" y1="650" x2="100" y2="100" stroke="#999" stroke-width="3"></line>
        <path d="M 90,100 L 100,80 L 110,100 " fill="#999"></path>
        <text x="80" y=90 fill="#999" writing-mode="vertical-rl">订单量</text>
    </g>
    <g id="xscale"></g>
    <g id="yscale"></g>
    <g id="rect">
    </g>
</svg>
// svg绘制条形统计图
//获取数据
const data = [
    {
        data: '星期一',
        order: "1000",
    },
    {
        data: '星期二',
        order: "2000",
    },
    {
        data: '星期三',
        order: "888",
    },
    {
        data: '星期四',
        order: "2100",
    },
    {
        data: '星期五',
        order: "500",
    },
    {
        data: '星期六',
        order: "3000",
    },
    {
        data: '星期日',
        order: "2500",
    },
]
//绘制x轴
let xlength = 800 / data.length;
let xscale = document.getElementById('xscale');
for (let i = 0; i < data.length; i++) {
    let xline = document.createElement('line');
    xline.setAttribute('x1', 150 + xlength * i);
    xline.setAttribute('y1', 600);
    xline.setAttribute('x2', 150 + xlength * i);
    xline.setAttribute('y2', 610);
    xline.setAttribute('stroke', '#999');
    xline.setAttribute('stroke-width', '3');
    xscale.appendChild(xline);
    
    let xtext = document.createElement('text');
    xtext.setAttribute('x', 140 + xlength * i - 10);
    xtext.setAttribute('y', 630);
    xtext.setAttribute('fill', '#999');
    xtext.innerHTML = data[i].data;
    xscale.appendChild(xtext);
	//添加到xscale中
    xscale.innerHTML += xline;
}
//绘制y轴
let ylength = 500 / data.length;
let yscale = document.getElementById('yscale');
for (let i = 0; i < data.length; i++) {
    let yline = document.createElement('line');
    yline.setAttribute('x1', 90);
    yline.setAttribute('y1', 600 - ylength * i);
    yline.setAttribute('x2', 100);
    yline.setAttribute('y2', 600 - ylength * i);
    yline.setAttribute('stroke', '#999');
    yline.setAttribute('stroke-width', '3');
    yscale.appendChild(yline);

    let ytext = document.createElement('text');
    ytext.setAttribute('x', 50);
    ytext.setAttribute('y', 600 - ylength * i + 5);
    ytext.setAttribute('fill', '#999');
    ytext.innerHTML = 500 * i;
    yscale.appendChild(ytext);
	//添加到yscale中
    yscale.innerHTML += yline;
}
//绘制矩形
let rect = document.getElementById('rect');
for (let i = 0; i < data.length; i++) {
    let rect1 = document.createElement('rect');
    rect1.setAttribute('x', 125 + xlength * i);
    rect1.setAttribute('y', 600 - data[i].order / 500*ylength);
    rect1.setAttribute('width', 50);
    rect1.setAttribute('height', data[i].order / 500*ylength);
    //随机颜色
    let color = '#' + Math.floor(Math.random() * 0xffffff).toString(16);
    rect1.setAttribute('fill', color);
    rect.appendChild(rect1);
	//添加到rect中
    rect.innerHTML += rect1;
}
上次编辑于:
贡献者: 林深不见鹿