How do I render a native svg from string in Konva? - konvajs

The Konva.Image.fromURL() method is the only way to render a native SVG mentioned in the documentation. I haven't found any other way to display an SVG from a variable without using 3rd party libraries.
Konva.Path cannot be used as an SVG consists of multiple elements and the library fails to render it correctly.
Using 3rd party libraries is not a good option either, as they reduce the picture quality and are not native methods.
Can anyone help me with finding a native way to render an SVG without compromising its quality, please?
example from docs:
Konva.Image.fromURL('./test.svg', (imageNode) => {
layerRef.current.add(imageNode);
imageNode.setAttrs({
width: 150,
height: 150,
x: 10,
y: 10,
});
layerRef.current.batchDraw();
its works
my example:
`const svg = <svg ... </svg>
Konva.Image.create(svg, (imageNode) => {
layerRef.current.add(imageNode);
imageNode.setAttrs({
width: 150,
height: 150,
x: 10,
y: 10,
});
layerRef.current.batchDraw();`
code in sandbox

You can convert svg string into data url using this:
function svgToURL(s) {
const uri = window.btoa(unescape(encodeURIComponent(s)));
return "data:image/svg+xml;base64," + uri;
}
When you have url of the SVG, you can use it as source for image.
const url = svgToURL(svg);
Full example using react-konva and use-image hook:
import React from "react";
import { Stage, Layer, Image } from "react-konva";
import useImage from "use-image";
function svgToURL(s) {
const uri = window.btoa(unescape(encodeURIComponent(s)));
return "data:image/svg+xml;base64," + uri;
}
const svg =
'<svg width="500" height="100" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg"><path fill="red" d="M3.9 7.8L6.4 7.8L6.4 9.2L3.9 9.2L3.9 18.7L2.3 18.7L2.3 9.2L0.5 9.2L0.5 7.8L2.3 7.8L2.3 6.3Q2.3 5.4 2.7 4.7Q3.2 4.1 3.9 3.8Q4.6 3.5 5.3 3.5L5.3 3.5Q6.0 3.5 6.3 3.6Q6.7 3.7 6.9 3.8L6.9 3.8L6.4 5.2Q6.3 5.2 6.1 5.1Q5.9 5.1 5.5 5.1L5.5 5.1Q4.7 5.1 4.3 5.5Q3.9 5.9 3.9 6.7L3.9 6.7L3.9 7.8ZM11.2 7.8L13.6 7.8L13.6 9.2L11.2 9.2L11.2 18.7L9.5 18.7L9.5 9.2L7.7 9.2L7.7 7.8L9.5 7.8L9.5 6.3Q9.5 5.4 9.9 4.7Q10.4 4.1 11.1 3.8Q11.8 3.5 12.6 3.5L12.6 3.5Q13.2 3.5 13.6 3.6Q14.0 3.7 14.1 3.8L14.1 3.8L13.7 5.2Q13.5 5.2 13.3 5.1Q13.1 5.1 12.7 5.1L12.7 5.1Q11.9 5.1 11.5 5.5Q11.2 5.9 11.2 6.7L11.2 6.7L11.2 7.8ZM18.4 7.8L20.8 7.8L20.8 9.2L18.4 9.2L18.4 18.7L16.7 18.7L16.7 9.2L14.9 9.2L14.9 7.8L16.7 7.8L16.7 6.3Q16.7 5.4 17.1 4.7Q17.6 4.1 18.3 3.8Q19.0 3.5 19.8 3.5L19.8 3.5Q20.4 3.5 20.8 3.6Q21.2 3.7 21.4 3.8L21.4 3.8L20.9 5.2Q20.8 5.2 20.5 5.1Q20.3 5.1 19.9 5.1L19.9 5.1Q19.1 5.1 18.7 5.5Q18.4 5.9 18.4 6.7L18.4 6.7L18.4 7.8Z "/><line x1="0" y1="20.59214501510574" x2="21.647727272727273" y2="20.59214501510574" stroke="red" stroke-width="2" stroke-linecap="square"/></svg>';
const url = svgToURL(svg);
function App() {
const [image] = useImage(url);
return (
<div>
<Stage
width={window.innerWidth}
height={500}
style={{ border: "4px solid blue" }}
>
<Layer>
<Image image={image} />
</Layer>
</Stage>
</div>
);
}
export default App;
Demo: https://codesandbox.io/s/happy-bassi-ojmjkl?file=/src/App.js

Related

google-map-react not loading- Uncaught TypeError: Cannot read properties of undefined (reading 'getChildren')

import React from "react";
import GoogleMapReact from "google-map-react";
const AnyReactComponent = ({ text }) => <div>{text}</div>;
export default function AppMapPage() {
const defaultProps = {
center: {
lat: 10.99835602,
lng: 77.01502627,
},
zoom: 11,
};
return (
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{ key: "my key" }}
defaultCenter={defaultProps.center}
defaultZoom={defaultProps.zoom}
>
<AnyReactComponent lat={59.955413} lng={30.337844} text="My Marker" />
</GoogleMapReact>
</div>
);
}
I have been trying to solve it for a while. I am trying to run simple example of google-map-react. But, This does not load maps. Instead gives following errors and the page is blank.
google_map_markers.js:100 Uncaught TypeError: Cannot read properties of undefined (reading 'getChildren')
at o._getState (google_map_markers.js:100:1)
at new o (google_map_markers.js:248:1)
at constructClassInstance (react-dom.development.js:13522:1)
at updateClassComponent (react-dom.development.js:20497:1)
at beginWork (react-dom.development.js:22440:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4161:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4210:1)
at invokeGuardedCallback (react-dom.development.js:4274:1)
at beginWork$1 (react-dom.development.js:27405:1)
at performUnitOfWork (react-dom.development.js:26513:1)
To resolve these issues, you have to remove *<React.StrictMode>* in the index.js file.
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>);
To
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
it's due to versioning. I downgraded from 18 to 17 and it worked. Man sometimes open source code consumes your all day.
I Just removed the <React.StrictMode> in the index.js file and it worked for me.

Phonegap + Framework7 + backgroundGeolocation

I'm trying to use https://github.com/mauron85/cordova-plugin-background-geolocation with Phonegap together with a Framework7 based UI.
That plugin has a very nice example for a React app. But I can't use it with just a basic Phonegap app (plus Framework7)
I looks like the whole plugin is not available at all.
For example in the code below console.log("222") is never shown.
$$(document).on('deviceready', function() {
console.log("111")
backgroundGeolocation.configure(callbackFn, failureFn, {
desiredAccuracy: 10,
stationaryRadius: 20,
distanceFilter: 30,
interval: 60000
});
console.log("222")
var callbackFn = function(location) {
console.log(location);
};
console.log("333")
var failureFn = function(error) {
console.log('BackgroundGeolocation error');
};
})
I have added the plugin with:
phonegap plugin add cordova-plugin-mauron85-background-geolocation
The config.xml has the line:
<plugin name="cordova-plugin-mauron85-background-geolocation" spec="~2.3.3"/>
And inside the plugins folder I can see the plugin installed.
But how can I start using it?

(React Native) Load local HTML file into WebView

I try to load the local .html file into WebView in React Native:
// load local .html file
const PolicyHTML = require('./Policy.html');
// PolicyHTML is just a number of `1`
console.log('PolicyHTML:', PolicyHTML);
// will cause an error: JSON value '1' of type NSNumber cannot be converted to NSString
<WebView html={PolicyHTML} />
The .html file should be read as a string, not as a resource representative.
How can I load the .html file into WebView in React Native?
By the way, what is the type of those resource representatives from require()? Is it number?
try it:
const PolicyHTML = require('./Policy.html');
<WebView
source={PolicyHTML}
style={{flex: 1}}
/>
I come across this post searching for loading static html.
If your html code is retrieved using, for example, an API, you can render WebView in this way:
<WebView
originWhitelist={['*']}
source={{ html: html, baseUrl: '' }}
/>
Notice that originWhitelistis required as explained in the documentation:
Note that static html will require setting of originWhitelist for
example to ["*"].
<View style={{flex: 1}}>
<WebView
style={{flex: 1}}
source={require("./resources/index.html")}
/>
</View>
To make WebView, the parent has to has a dimension or flex:1. We could set the WebView to flex: 1 too so that it fills up the parent.
If you need to serve local assets as well, then:
put all assets together with index.html into android/app/src/main/assets/www (You can copy them there with gradle task)
Then:
var uri = Platform.OS == "android" ?
"file:///android_asset/www/index.html" :
"./web/www/index.html"
return <WebView source={{ uri }} />
** For iOS didn't tested, please add instruction, how assets should be stored
With Expo tools and generally using Expo:
import { WebView } from "react-native-webview";
import { readAsStringAsync } from "expo-file-system";
import { useAssets } from "expo-asset";
export const MusicSheet = () => {
const [index, indexLoadingError] = useAssets(
require("../assets/musicsheetview/index.html")
);
const [html, setHtml] = useState("");
if (index) {
readAsStringAsync(index[0].localUri).then((data) => {
setHtml(data);
});
}
return (
<View style={styles.container}>
<WebView
onLoad={() => {}}
source={{ html }}
onMessage={(event) => {}}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
height: 100,
display: "flex",
},
});
Try this :
Add your .html file in your project.
Write such lines of code in the file where you want to use WebView Component
const OurStoryHTML = require ('./OurStory.html')
<WebView
source={OurStoryHTML}
style={{flex: 1, marginTop : 44}}
/>
It may help you.
If you're working with assets, project directories is different on the device's directory once the project is build and you can't simply reference them via string url.
Expo
If using expo, you have to require every asset then use useAssets on the require to cache them to the local storage of the device.
useAssets will return an object that contains a localUri
(this is the uri of the image that has been cached)
you can then use the localUri and put it as the src of the image
import { useAssets } from 'expo-asset';
/* . . . */
const IndexHTML = require('./assets/index.html');
const myImage = require('./assets/splash.png');
// url link after image is cached to the device
const [imgSrc, setImgSrc] = useState('');
const [image, imerr] = useAssets(myImage);
const [html, error] = useAssets(IndexHTML);
const webViewProps = {
javaScriptEnabled: true,
androidLayerType: 'hardware',
originWhitelist: ['*'],
allowFileAccess: true,
domStorageEnabled: true,
mixedContentMode: 'always',
allowUniversalAccessFromFileURLs: true,
onLoad: () => {
console.log(image[0].localUri);
setImgSrc(image[0].localUri);
},
source: {
html: '<img src="' + imgSrc + '"/>',
},
};
return <WebView {...webViewProps} />
const webViewProps = {
...
source: IndexHTML,
};
Note: for the expo apporach, files referenced in IndexHTML will not be found
The trick is to turn your html into a string literal to utilize template strings.
Then you have to manually require each of those assets to concatenate localUrl
require() has limited types supported and you need to add a metro.config.js in your root folder.
it will give errors if you require() a .js file since it reads it as a module rather, the workaround approach would be to bundle your assets
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.assetExts.push(
// Adds support for asset file types
'css', 'ppt', 'obj'
);
module.exports = config;
Moreover, expo can hot reload changes done with the cached assets.
React Native
If you have the android folder in your directory, navigate to
android > app > build.gradle
then add
android {
/* link assets to the local storage of device */
sourceSets {
main { assets.srcDirs = ['src/main/assets', '../../source/assets/'] }
// [do not touch, 'relative to your asset'] }
}
. . .
finally, the relative folder you linked in gradle can be accessed through
file:///android_asset/
for ex. file:///android_asset/index.html -> /asset/index.html
return <WebView source={{uri: `file:///android_asset/index.html`}} />
For IOS, here's how
On the other hand, you have to rebuild vanilla react to see the changes in the assets.. which takes about 20 minutes or so
Alternative
A quick solution would be to integrate a static server, but this is a recent fork of the react-native-static-server that only works in vanilla react native.

Why is cordova-plugin-file giving me null for cordova.file.dataDirectory?

Running on an iPhone 6 through Xcode 7.3 and testing in Safari Web Inspector, I am using the Cordova File plugin. I am running example code copied right out of the cordova-plugin-camera docs for saving an image:
function createNewFileEntry(imgUri) {
console.log("saveImage->createNewFileEntry");
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function(dirEntry) {
console.log("createNewFileEntry dir: " + dirEntry);
// name the file
var d = getDateTime();
var fileName = "cc" + d + ".jpeg";
// JPEG file
dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
console.log("createNewFileEntry file: " + fileEntry.fullPath);
writeFile(dstFileEntry, imgUri);
}, function() { console.log("Error: createNewFileEntry->getFile"); });
}, function() { console.log("Error: createNewFileEntry->resolveLocalFileSystemURL"); });
}
What happens is that window.resolveLocalFileSystemURL()fails because directory spec cordova.file.dataDirectory is null. In fact ALL possible cordova.file.* directories are null. Why?
The following graphic should answer relevant questions- cordova 4.1.1 for iOS is running, cordova File is present, window.resolveLocalFileSystemURL() is valid.
Why are all the symbolic Directory names null?
This is the config.xml file that is at the root of the app, ./config.xml
This config.xml is in the ios platform, ./platforms/ios/PayLive/config.xml

Detect lower versions of Internet Explorer in mvc 4

I just created mvc 4 application. In that application I want to show alert,if user open this web application using Internet Explorer 6,7 and 8
This is the code I put top of "_Layout.cshtml" file
#if ((Request.Browser.Browser == "IE") & ((Request.Browser.Version == "8.0") | (Request.Browser.Version == "7.0") | (Request.Browser.Version == "6.0")))
{
<script type='text/javascript'>
alert("You're using older version of Internet Explorer")
</script>
}
But this is not checking internet explorer version , it gives me pop up for almost all the versions of Internet Explorer ( Edge(11) , 10 , 9 , 8 , 7, 6 )
How can I filter those versions separately
Try this-
#if (Request.Browser.Browser == "IE" && Convert.ToDouble(Request.Browser.Version) < 7.0)
{
<script type='text/javascript'>
alert("You're using older version of Internet Explorer")
</script>
}
Edit-
This checks if current version of IE is lower than 7.0, You can change the version number accordingly.
Edit 2-
I just realized my browser was named as InternetExplorer, So I changed following-
#if (Request.Browser.Browser == "InternetExplorer" && Convert.ToDouble(Request.Browser.Version) < 7.0)
{
<script type='text/javascript'>
alert("version is not lower");
</script>
}
The following JavaScript should do what you need:
<script type='text/javascript'>
var div = document.createElement("div");
div.innerHTML = "<!--[if lt IE 9]><i></i><![endif]-->";
var isIeLessThan9 = (div.getElementsByTagName("i").length == 1);
if (isIeLessThan9) {
alert("You're using older version of Internet Explorer");
}
</script>

Resources