Fill SVG path based on the length of the video? - javascript

I'm trying to create a custom progress bar for the HTML5 video tag. The progress bar doesn't need any controls, it's just an SVG that I'd like to fill as the video progresses.
The video is a React component:
import React, {useRef} from 'react'
const VideoSection = () => {
const videoRef = useRef()
const handlePlayVideo = () => videoRef.current.play()
return (
<div>
<button onClick={handlePlayVideo}>
Play video
</button>
<video src='./video.mp4' ref={videoRef}/>
//The SVG I'd like to animate
<svg width="595" height="28" viewBox="0 0 595 28" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M0 14C0 6.26801 6.26801 0 14 0H595V28H14C6.26801 28 0 21.732 0
14V14Z" fill="#072760"/>
</svg>
</div>
)
}
export default VideoSection
Or is there an alternative way to approach/build the progress bar? Thanks!

Related

jsPDF not taking svg as DOM element

I am using jsPDF in React js. I am crating some content and drawings inside a <svg>. When I want to render the pdf, everything inside the SVG is not rendered. Is there any solution to solve this or another one to export the SVG to pdf? I want the text inside the pdf to be selectable as well, I don't want just to transform the SVG into a picture and drop it into a pdf, that is not a solution. Below is my code that is not working
import React from 'react'
import jsPDF from 'jspdf'
const About = () => {
const downloadPDF = () => {
let doc = new jsPDF("p","pt","a4")
doc.html(document.querySelector("#graph"), {
callback: function(pdf){
pdf.save("doc.pdf");
}
})
}
return (
<div id = "graph">
Some text // {/*This is rendered */}
<h1>some title</h1> {/*This is rendered */}
<svg width="500" height="500"> {/*This is not rendered */}
<circle cx="50" cy="80" r={radius} stroke="rgb(0,125,225)" stroke-width="2" fill="red"/>
</svg>
</div>
)
}
export default About

Best way to import SVG icons into a Svelte app

I have about 80 custom SVG icons that I'm importing into a Svelte front-end app. Building on https://github.com/sveltejs/template, it's built with Rollup and includes Typescript, Tailwind, and all the modern goodies.
The dilemma is how to add the icons into the app. As SVGs, the icons are short XML text strings that do not exceed 2kB.
Option 1: as image assets
Upload all the icons as foo.svg into public/assets/icons.
Create a svelte component <Icon type="foo' /> that displays an the icon using <img src="foo.svg>.
This approach means that the icons are not part of the code.
Benefits: icons can be dynamically loaded by frontend code on demand. No need to bundle all icons into app code.
Cons: slow page load if there are a lot of new icons, and the browser has to fetch a dozen 1kB files. Deploying the app as a PWA means we need to manually tell it to cache the icons beforehand.
Option 2: as part of the app build
Use something like https://github.com/cristovao-trevisan/svelte-icon or https://github.com/codefeathers/rollup-plugin-svelte-svg to directly import each icon into code: import Home from './icons/home.svg';
Create a svelte component that selects the right imported component or SVG string and displays it.
Here, the icons are bundled as text strings with the app itself.
Benefits: Icons are delivered as part of the app bundle. Caching is unnecessary. Possible to dynamically modify some of the icon code e.g. colors, viewBox, etc on load.
Cons: It's unnecessary to include all icons in the app to reduce time to first byte. Can't do bundle splitting, etc. without adding more complexity. Makes the rendering slower because Javascript code needs to first turn a string into an SVG instead of just loading an image.
Questions
It seems that building icons in tthe app is a better way from this analysis, but have I missed something?
Does the calculus change if the "icons" are detailed images that are 50-100kB instead of the tiny strings here?
The following approach has these advantages:
One central point to maintain all your icons for your app
Reduced network requests for fetching SVG icons
Reusable icons throughout the app without having duplicate svg elements
Have a dedicated Icon.svelte component setup like this:
<script>
export let name;
export let width = "1rem";
export let height = "1rem";
export let focusable = false;
let icons = [
{
box: 24,
name: "save",
svg: `<g stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></g>`
},
{
box: 32,
name: "trash",
svg: `<path d="M12 12h2v12h-2z" /><path d="M18 12h2v12h-2z" /><path d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z" /><path d="M12 2h8v2h-8z" />`
}
];
let displayIcon = icons.find((e) => e.name === name);
</script>
<svg
class={$$props.class}
{focusable}
{width}
{height}
viewBox="0 0 {displayIcon.box} {displayIcon.box}">{#html displayIcon.svg}</svg>
You can then use it like so:
<Icon name="trash" class="this-is-optional" />
You can just change the file extension to .svelte and import an SVG as a normal component.
another way is to use a symbol defs file (ex: icons.svg) in your public folder.
then in your code do something like this :
Icon.svelte
<script>
export let name;
export let width = "1.5rem";
export let height = "1.5rem";
export let focusable = false;
</script>
<svg class={$$props.class} {focusable} {width} {height}>
<use href={`/icons.svg#${name}`} />
</svg>
icons.svg
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-warning" viewBox="0 0 20 20">
<path d="M19.511 17.98l-8.907-16.632c-0.124-0.215-0.354-0.348-0.604-0.348s-0.481 0.133-0.604 0.348l-8.906 16.632c-0.121 0.211-0.119 0.471 0.005 0.68 0.125 0.211 0.352 0.34 0.598 0.34h17.814c0.245 0 0.474-0.129 0.598-0.34 0.124-0.209 0.126-0.469 0.006-0.68zM11 17h-2v-2h2v2zM11 13.5h-2v-6.5h2v6.5z">
</path>
</symbol>
</defs>
</svg>
App.svelte
<Icon name="icon-warning" />
this way you use one http call to load svg file. and then just use the part you need in markup.
Using like {#html SVG_CODE} for rendering a svg code from another component or variable is a Bad choice . This sometimes gives risk of XSS attack . Specially when the image comes from external source.
check : https://github.com/sveltejs/svelte/issues/2545
Svelte doesn't perform any sanitization of the expression inside {#html ...} before it gets inserted into the DOM. In other words, if you use this feature it's critical that you manually escape HTML that comes from sources you don't trust, otherwise you risk exposing your users to XSS attacks.(source)
I think using plugin like rollup-plugin-svelte-svg would be a better choice
1.Install using npm
npm i -D rollup-plugin-svelte-svg
2.Simply call svelteSVG before svelte in your rollup config.
export default {
plugins: [
svelteSVG({
// optional SVGO options
// pass empty object to enable defaults
svgo: {}
}),
],
...
}
3.You can then import svg in your JS thusly:
<script>
import Logo from "./logo.svg";
</script>
<Logo width=20 />
Or
Open your svg image using text editor, copy all the code and paste in a file name with .svelte extension .
for example ,
icon.svg file contains :
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
Copy it in a icon.svelte file
<script></script>
<style></style>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
When you want use icon.svelte component
<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon />
If you want you can pass optional class
then in icon.svelte
<script></script>
<style></style>
<svg class="{$$props.class}" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
When you want to call icon.svelte component
<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon class="h-6 w-6" />
A working solution is to hold the SVGs in a separate IconService.js:
export const chevron_down = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-down\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>";
export const chevron_right = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-right\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg>";
This can easily be imported and used within svelte #html function.
<script>
import {chevron_down, chevron_right} from 'IconService';
</script>
{#html chevron_down}
A Svelte Component with SVG Icons inside:
<script>
export let i
export let stroke = 3
</script>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width={stroke} stroke="currentColor" class={`w-6 h-6 ${$$props.class}`}>
{#if i == 'search'}
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" />
{:else if i == 'plus'}
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
{:else}
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636" />
{/if}
</svg>
Use like this:
<Icon i='search'/>
<Icon i='plus' stroke={5} class="w-10 h-10 text-slate-500"/>
Benefits
All icons are collected in a single file. The SVG-Paths are compiled as HTML-Code and not as inline strings. This also displays any errors in the SVG in the IDE. When you add a new Icon you just have to copy the contents of the SVG into this file.
Programmatically loading the svg as string from one module (iconsProvider.js) which is exporting all svgs as constants.
So basically, with every new svg, you only need to export it from iconprovider.js. And then pass icon.svelte the right iconName prop.
icon.svelte
<script lang="ts">
import * as iconsProvider from "../lib/iconsProvider"
export let iconName: string
</script>
{#html iconsProvider[ iconName ]}
iconsProvider.js
export const icon1 = `<svg ...>`
export const icon2 = `<svg ...>`
Hope this helps.

Dynamically draw/turn SVG with variable input

I want to dynamically draw/turn an SVG with React from degree paramters that im recieving. Its like the direction arrow from a weather-info.
How do I dynamically draw an SVG in Javascript / ReactJS?
How do I turn the SVG with variable input?
I'm aware, that you can draw SVG-Graphics like this:
function draw_square() {
var draw = SVG('square_1');
draw.size(120, 120);
var square = draw.rect(100, 100);
square.attr({ fill: '#f06' });
}
draw_square()
but how do I connect different svg-paths to each other?
I recommend a slightly different approach:
const WindDirectionIcon = ({ width, height }) => {
return (
<svg
id="wind-direction-arrow"
width={width}
height={height}
viewBox="0 0 12 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M6 0L0 12L6 9L12 12L6 0Z" fill="black" />
</svg>
);
};
export default function App() {
useEffect(() => {
const degrees = 90; // Retrieve from your API
const arrow = document.getElementById("wind-direction-arrow");
arrow.setAttribute("transform", `rotate(${degrees})`);
}, []);
return (
<div className="App">
<WindDirectionIcon width="24" height="24" />
</div>
);
}
So the svg itself can be composed of a single path and the only thing we do with javascript is to set the transform attribute to rotate a certain number of degrees based on the API data you receive using template literals.
Rotate with javascript based on this answer.
Sandbox Example

Parse SVG file to React component

I have an SVG file: check.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28.3 28.3"><path d="M25.2 3.638l-1.6 1.6c-4.3 4.3-8.9 9.2-13.2 13.6l-5.9-4.9-1.7-1.4-2.8 3.3 1.7 1.4 7.5 6.2 1.6 1.3 1.4-1.4c4.8-4.8 9.9-10.4 14.6-15l1.6-1.6-3.2-3.1z"/></svg>
I want a React component from it; but I don't want any wrapper tag, just
<svg>
<path […]/>
</svg>
I know I can do something like
const renderSvg = svg => React.createElement('svg', {
dangerouslySetInnerHTML: { __html: svg }
});
or even easier
const RenderSvg = (svg) => <svg dangerouslySetInnerHTML={{ __html: svg }} />;
But I would end up with:
<svg>
<svg>
<path […]/>
</svg>
</svg>
Does anyone know how to achieve this without using an external library?
Have your SVG in a separate file, than import that as a component, like this:
import {ReactComponent as Circle} from "./circle.svg"
Following is a demo sandbox

How to loop images with react-native-svg

I'm working on a project with react native and we chose to use react-native-svg in order to display our map. Everything's worked perfectly so far, except for this :
I have an array of objects that each have coordinates and a size. I want to loop through this array to display an image at all of these coordinates. But no luck, I managed to diplay shapes and texts but no images.
Here is the render function of my parent element :
render() {
return (
<View>
<Svg
height={screen.height}
width={screen.width}
>
<G
width={mapSize.x}
height={mapSize.y}
x={this.state.center.x}
y={this.state.center.y}
originX={mapSize.x / 2}
originY={mapSize.y / 2}
rotation={this.state.orientation}
scale={1}
>
<Rect
width={mapSize.x}
height={mapSize.y}
x={0}
y={0}
scale={1}
fill="#0071e9"
/>
<G
width={mapSize.x}
height={mapSize.y}
x={this.state.cnv.x}
y={this.state.cnv.y}
scale={1}
>
// this is my child component's tag
<Islands
islands={this.state.contentToRender}
deg={this.state.orientation}
/>
</G>
</G>
<Image
x={(screen.width / 2) - 50}
y={-(screen.height / 2) + 94.46}
width="100"
height="189.9"
preserveAspectRatio="xMidYMid slice"
href={images.bateau}
/>
</G>
</Svg>
</View>
)
}
}
here is my child component's render :
render(){
const that = this
return(
this.props.islands.map((c) => {
return (
<Image
key={ c.id }
x={ c.position.x }
y={ c.position.y }
width={ c.size.x }
height={ c.size.y }
preserveAspectRatio="xMidYMid slice"
rotation={-that.props.deg}
originX={ c.position.x + (c.size.x / 2) }
originY={ c.position.y + (c.size.y / 2) }
href={images.rouge}
/>
)
})
)
}
And finally my images.js file :
const images = {
boussole: require('./img/Boussole.png'),
aiguille: require('./img/Aiguille.png'),
bateau: require('./img/Bateau.png'),
home: require('./img/home.png'),
cyclop_island: require('./img/ile_cyclope_navig.png'),
rouge: require('./img/smiley-carre-rouge-simple-face-magnetique.jpg'),
}
export default images
I have no issue with any aspect of the Svg component, I can display every image of the images.js file as long as they are not in a loop.
Also, even if the image doesn't display the console shows no error.

Categories

Resources