SVG is a vector graphic representation, and one of its strengths is that the path tag can represent any vector shape. Using the path tag can do a lot of things that traditional HTML/CSS can’t. Here are a few examples.

1. Animate paths

I added this implementation to the SVG navigation Underline cursor Follow effect post, and the final effect looks like this:

The implementation code is as follows:

Animation using animateMotion and path, as described above.

2. Achieve irregular shape click

As shown in the figure below, it is necessary to achieve the effect of clicking on the continent to enter the continent, for example, clicking on Africa to enter Africa:

We can use div to set a box on the top of Africa, but div can only be regular square, can not achieve the point to the African continent when entering, but the outline of the continent is irregular, so traditional HTML is not able to solve this problem. However, using SVG path can solve this problem. Method 1 is to listen for path click events, as shown below:

Because this outline can be followed by the UI, they are generally drawn by AI/PS and other vector software, let them guide an SVG to you.

Method 2 is to call the isPointInFill API of SVG to determine whether the clicked point is in the fill field of the Path. This can also be done, but it is more cumbersome than method 1, since the mouse position needs to be converted to the position of the SVG view.

3. Drag and drop interactions along paths

In point 1, the animation along the path is an automatic process. Is there any way for the user to drag it over by himself?

This kind of scene has volume control and so on need to have percentage control. You can start by using one of SVG’s online tools to draw a graph like this:

To get the SVG code:

<svg class="volumn-controller" width="580" height="400" xmlns="http://www.w3.org/2000/svg">
    <path class="volumn-path" stroke="# 000" d="M100 approximately, c93, 247-128, 284-129, 388, 6" opacity="0.5" stroke-width="1" fill="#fff"/>
    <circle class="drag-button" r="12" cy="247" cx="100" stroke-width="1" stroke="# 000" fill="#fff"/>
 </g>
</svg>Copy the code

The key here is the d property in the path tag, which is short for data and defines the shape of the path. There are a number of properties that can be used to control the shape, as shown in the following figure:

To achieve this interaction, you need to dynamically change the center position of the circle (cx, cy) to the corresponding place on the path. SVG does not provide an API directly, but it does provide an API that can be used indirectly, called getPointAtLength, to pass a length argument, as shown in the following code:

let volumnPath = document.querySelector('.volumn-path');
// Outputs the coordinates of path at length 100
console.log(volumnPath.getPointAtLength(100));
// Displays the total length of the current path
console.log(volumnPath.getTotalLength());Copy the code

Console output:

Change the cx/cy of the circle to the x/y coordinate above, and the circle will go to the corresponding position:

The problem here is that the LENGTH parameter passed by the API is relative to the length of the curve, but the mouse position is linear, so there is no direct way to know how far the mouse is from the beginning of the curve.

Therefore, we need to calculate. In this scene, we can take the corresponding position of the mouse’s X coordinate on the curve, as shown in the picture below:

So here’s the idea, you can calculate the coordinates of this path every pixel length, and then store it in an array. Since the x-coordinate is known when the mouse moves, you can look up the y-coordinate of the corresponding x-coordinate in this array and get the desired center position.

So let’s evaluate it and save it to an array:

let $volumnController = document.querySelector('.volumn-controller'),
    $volumnPath = $volumnController.querySelector('.volumn-path');
// Get the total length of the current path
let pathTotalLength = $volumnPath.getTotalLength() >> 0;
let points = [];
// Start position with length 0
let startX = Math.round($volumnPath.getPointAtLength(0).x);
// Save the coordinates of the points on the path every pixel distance
for (let i = 0; i < pathTotalLength; i++) {
    let p = $volumnPath.getPointAtLength(i);
    // The saved coordinates are rounded to balance the error
    points[Math.round(p.x) - startX] = Math.round(p.y);
}Copy the code

This is stored in a p0ints array whose index is the x coordinate and value is the y coordinate. In this example, the total length is 451.5px and the resulting points array is 388. You can save coordinates at intervals of 0.5px, but in this case 1px will suffice.

Then listen to the mouse event, get the x coordinate, query the Y coordinate, and dynamically change the center position of the circle, as shown in the following code:

let $dragButton = $volumnController.querySelector('.drag-button'),
    // Get the starting position relative to the current window, equivalent to jquery.fn.offset
    dragButtonPos = $dragButton.getBoundingClientRect();
function movePoint (event) {
    // The current mouse position minus the starting position of the center of the circle to get the shift deviation, 12 is the radius value
    let diffX = event.clientX - Math.round(dragButtonPos.left + 12);
    // We need to make a boundary judgment
    diffX < 0 && (diffX = 0);
    diffX >= points.length && (diffX = points.length - 1);
    // startX is the position with length 0 obtained in the code above
    $dragButton.setAttribute('cx', diffX + startX);
    // Use the points array to get the y coordinates
    $dragButton.setAttribute('cy', points[diffX]);
}
$dragButton.addEventListener('mousedown'.function (event) {
    document.addEventListener('mousemove', movePoint);
});
document.addEventListener('mouseup'.function () {
    document.removeEventListener('mousemove', movePoint);
});Copy the code

There are two coordinate systems, one is relative to the page window, its origin (0, 0) coordinate point is the upper left corner of the current page visual area (client), the second coordinate system is the coordinate system of SVG, Its origin position (0, 0) is in the upper left corner of the SVG canvas, as shown below:

The mouse position is relative to the view client, so we need to get the position of the circle on the client using the native getBoundingClient. Then subtract the circle’s clientX from the mouse’s clientX to get the correct diff. Add the diff value to the circle’s starting position in SVG to get the SVG x position. Then check the points array to get the Y position and set the Cx /cy value of the circle.

The implementation of this is fairly simple, about 30 lines of code. Note that if the SVG is scaled, the coordinates will be scaled accordingly. So it’s best not to zoom, 1:1 display is much easier.

What if you want to display specific volume values? This is easy to do, just need to save the point coordinates in the first step when the length of the path is also saved, the final effect is as follows:

A full demo: SVG-path-drag.html

What if the path is complicated? One x-coordinate might correspond to two points, as shown below:

There is a way to do this, the calculation method is similar, also need to take out every 1px point coordinates on the path, and then calculate the mouse position from which point coordinates are closest, and then take that point. Of course, when judging which point is the best, the algorithm needs to be optimized, instead of a for loop directly, as can be seen in this codepen.

4. Path deformation animation

Paths combined with keyframes can do some interesting things, such as this codepen example:

Hover (); transition (); transition ();

<svg viewBox="0 0 10 10." " class="svg-1">
  <path d="M2, 2 L8, 8" />
</svg>
<style>
.svg-1:hover path {
  d: path("The M8, 2 L2, 8");
}
path {
    transition: d 0.5 s linear;
}
</style>Copy the code

This kind of deformation transition animation is conditional, that is, its path data format should be consistent, how many M/L/C attributes should be consistent, otherwise it cannot do deformation animation.

5. Mask effect with Clip-path

Using CSS, you can only do some rounded masks with border-radius, which is implemented with overflow: hidden, but using clip-path + SVG path can make any shape mask, as shown in the following heart-shaped mask:

The following code looks like this:

<div style="width:200px; height:200px">
    <img src="photo.png" alt style="width:100%">
</div>
<style>
img {
    clip-path: url("#heart");
}
</style>Copy the code

The id #heart in style refers to an SVG clipPath, as shown below:

<svg xmlns="http://www.w3.org/2000/svg" width="0" height="0">
    <clipPath id="heart" clipPathUnits="objectBoundingBox">
        <path transform="Scale (0.0081967, 0.0101010)" d="M61.18795, c24.91828 24.08746, 57.29309, 122.5489 0 0,73.66254 c - 122.5489, 73.66254-24.91828, 130.95562 0, 73.66254 z"/>
    </clipPath>
</svg>Copy the code

To make the path fit exactly 100% of the width of the div container, set:

clipPathUnits="objectBoundingBox"Copy the code

This will cause the units in the d property to scale from 0 to 1, so we need to scale it down a little bit. The original width is 122, the height is 99, and we need the scale value to be (1/122, 1/99). So you’re going to be 100% full, and if you start with a ratio of 0 to 1, you don’t have to do that.

In addition, clip-path using SVG does not support deformation animation.

This article describes several effects you can do with SVG path: animate a path, click with irregular shapes, drag along a path, animate a path’s deformation, and mask some effects with clip-Path. SVG’s path effects are pretty powerful, so think SVG when you have effects you can’t do with HTML/CSS.

The Efficient Front End is in its second printing. I hear you haven’t bought it yet