I am trying to open a project folder image in the canvas without uploading it in a file input. But it is not showing there and I am using react-konva to add the image.
Here is the code where I am trying to upload the image:
const img = new window.Image();
img.src = require('../../../assets/images/71.png');
img.onload = function () {
const img_width = this.width;
console.log('the image', require('../../../assets/images/71.png'));
const img_height = this.height;
const max = img_width > img_height ? 100 : 100;
const ratio = (img_width > img_height ? (img_width / max) : (img_height / max))
setOriginalDim({ width: img_width, height: img_height });
setDimensions({ width: img_width/ratio, height: img_height/ratio });
const theImg = new Konva.Image({
image: this,
x: 0,
y: 0,
width: img_width/ratio,
height: img_height/ratio,
name: 'background_image',
draggable: true
});
if (layerRef.current != null) {
console.log('image', theImg);
layerRef.current.add(theImg);
layerRef.current.draw();
}
};
And my react component is this:
<Stage
width={dimensions.width}
height={dimensions.height}
onMouseDown={handleStageMouseDown}
style={{ backgroundColor: '#fff', boxShadow: '0 0 6px 0 #666' }}
ref={stageRef}
id="certificate_canvas"
>
<Layer
ref={layerRef}
/>
</Stage>
The onload part is not executing also the image is not loading into the canvas. How do I add the project folder image without uploading it
instead of this
img.src = require('../../../assets/images/71.png');
use
img.src = '../../../assets/images/71.png';
you dont need the require function
Related
new to openlayers, i am currently trying to grab all tiles at certain zoom level within the view extent and add a feature/icon to each tile location, i can grab tile coords using
src.getSource().getTileGrid().forEachTileCoord
problem is it returns Z,X,Y tile coords (ie [16, 31689, 20858]) and i cant find anyway to either set icons location with these coords or convert them to lon/lat (eg 'EPSG:4326'[-5.928909184411161, 54.5921176536682]
iv tried
ol.proj.toLonLat
but it returns 0.00014373044545912343, 0.284665959233962, 20858.
as im new i may have something fundamentally wrong with my setup, or im completely missing something,thanks
To get actual coordinates you can use getTileCoordExtent / getCenter. This will get you the center coordinates for each tile.
By default the coordinate format is EPSG:3857 and no conversion should be needed to add a Feature.
const zDirection = 0;
const tileLayer = new ol.layer.Tile({
source: new ol.source.TileDebug({
zDirection: zDirection,
})
});
const style = new ol.style.Style({
image: new ol.style.Circle({
radius: 70,
fill: new ol.style.Fill({color: 'rgba(255, 0, 0, .2)'}),
}),
});
const layer = new ol.layer.Vector({
style: style,
});
const map = new ol.Map({
target: 'map',
view: new ol.View({
center: [0, 5000000],
zoom: 2,
}),
layers: [tileLayer, layer],
});
map.on('moveend', function () {
const view = map.getView();
const tileGrid = tileLayer.getSource().getTileGrid();
const extent = view.calculateExtent();
const zoom = tileGrid.getZForResolution(view.getResolution(), zDirection);
const features = [];
tileGrid.forEachTileCoord(extent, zoom, function (tileCoord) {
const center = ol.extent.getCenter(tileGrid.getTileCoordExtent(tileCoord));
features.push(new ol.Feature(new ol.geom.Point(center)));
});
layer.setSource(new ol.source.Vector({features: features}));
});
html, body {
padding: 0;
margin: 0;
}
body {
display: flex;
justify-content: stretch;
height: 100vh;
width: 100vw;
}
#map {
flex: 1;
}
<head>
<link href="https://cdn.jsdelivr.net/gh/openlayers/openlayers#v6.14.1/src/ol/ol.css" rel="stylesheet"/>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.14.1/build/ol.js"></script>
</body>
TL;DR: In #react-google-maps/api, I want to be able to make dynamic cluster icons/symbol in the style of pie charts, based on the markers in the cluster, but it seems I can only make icons from a static array, and cannot pass the the markers as parameters.
Full Description:
I am using typescript react with the package #react-google-maps/api, and I'm trying to find a way with the ClustererComponent/MarkerClusterer to take a callback or similar in order to be able to be able to create an svg for each cluster based on the markers in the given cluster.
The current issue is that the way I understand it, I am limited to a static array of urls to icons, and thought I can make an svg in those, I have no way to pass parameters into those svgs, as the only way the package allows me to chose a style is thought index in the style array.
I have read thought the following material, but have not been able to get find a way to make an icon dynamically based on the markers:
Documentation for #react-google-maps/api: https://react-google-maps-api-docs.netlify.app/#markerclustere
Documentation for google maps markerclusterer: https://developers.google.com/maps/documentation/javascript/marker-clustering
I have found libraries like this: https://github.com/hassanlatif/google-map-chart-marker-clusterer, that should be able to be used as a solution, but they don't seem to work with the #react-google-maps/api, only with earlier versions of google map. If this is not the case, and these can be used directly, then I would be more then happy with an answer describing how to use libraries like the one above with #react-google-maps/api, as that should allow be to make clusters in the same way as the picture below.
EDIT: as I got reminded in the comments, here is the code I have so far:
What I've tried: I have tried to find any way to set in an svg element instead of a url, but have since just decided to make a url with the svg data, as shown below. I have tried to edit the url of the clusters under the MarkerClusterer thought the callback for onClusteringBegin, onClusteringEnd and onLoad, but so far, no luck.
How I make the svg into url-data, so it can be used for img src
/*
* Pie Chart SVG Icon in URL form
*
* Inspiration taken from: https://medium.com/hackernoon/a-simple-pie-chart-in-svg-dbdd653b6936
*
* Note: As of right now, I am identifying the difference in marker types by setting the type-number I use in the title of the marker
*/
const serializeXmlNode = (xmlNode: any) => {
if (typeof window.XMLSerializer != "undefined") {
return (new window.XMLSerializer()).serializeToString(xmlNode);
} else if (typeof xmlNode.xml != "undefined") {
return xmlNode.xml;
}
return "";
}
function getCoordinatesForPercent(percent: number) {
const x = Math.cos(2 * Math.PI * percent);
const y = Math.sin(2 * Math.PI * percent);
return [x, y];
}
const makePieChartIcon = (slices: any[]) => {
const svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg')
svg.setAttribute('viewBox', '-1.1 -1.1 2.2 2.2')
svg.setAttribute('style', 'transform: rotate(-90deg)')
svg.setAttribute('height', '60')
var circle = document.createElementNS(svgNS, 'circle')
circle.setAttribute('r', '1.1')
circle.setAttribute('fill', 'white')
svg.appendChild(circle);
let cumulativePercent = 0;
slices.map((slice: any) => {
const [startX, startY] = getCoordinatesForPercent(cumulativePercent);
cumulativePercent += slice.percent;
const [endX, endY] = getCoordinatesForPercent(cumulativePercent);
const largeArcFlag = slice.percent > .5 ? 1 : 0;
const pathData = [
`M ${startX} ${startY}`, // Move
`A 1 1 0 ${largeArcFlag} 1 ${endX} ${endY}`, // Arc
`L 0 0`, // Line
].join(' ');
const path = document.createElementNS(svgNS, 'path');
path.setAttribute('d', pathData);
path.setAttribute('fill', slice.color);
svg.appendChild(path);
})
var svgUrl = 'data:image/svg+xml;charset=UTF-8,' + serializeXmlNode(svg)
return svgUrl
}
const makeDynamicClusterIcon = (markers: any[]) => {
var numMarkers = markers.length;
var slices = markers.reduce((acc: any, marker: any) => {
acc[parseInt(marker.title)].percent += 1 / numMarkers;
return acc;
}, [
{ percent: 0, color: 'Green' },
{ percent: 0, color: 'Blue' },
{ percent: 0, color: 'Red' },
])
var newIconURL = makePieChartIcon(slices)
return newIconURL;
}
How I use the MarkerClusterer Component
<MarkerClusterer
options={{
averageCenter: true,
styles: clusterStyles,
}}
>
{(clusterer) =>
markerData.map((marker: any) => (
<Marker
key={marker.key}
title={String(marker.type)}
position={{ lat: marker.lat, lng: marker.lng }}
clusterer={clusterer}
/>
))
}
</MarkerClusterer>
Right now, I can only use some static styles, but I have them as the following for testing:
const clusterStyles = [
{
height: 50, textColor: '#ffffff', width: 50,
url: 'data:image/svg+xml;charset=UTF-8,%3Csvg xmlns="http://www.w3.org/2000/svg" height="50" width="100"%3E%3Ccircle cx="25" cy="25" r="20" stroke="black" stroke-width="3" fill="green" /%3E%3C/svg%3E',
},
{
height: 50, textColor: '#ffffff', width: 50,
url: 'data:image/svg+xml;charset=UTF-8,%3Csvg xmlns="http://www.w3.org/2000/svg" height="50" width="100"%3E%3Ccircle cx="25" cy="25" r="20" stroke="black" stroke-width="3" fill="red" /%3E%3C/svg%3E',
}
];
I found a solution, by finding out that the style array for each cluster (ClusterStyles) can be changed, and then I have change it with the data from the specific markers in the given cluster. I ended up doing this in the callback onClusteringEnd, as here:
{/* Added to the MarkerClusterer */}
onClusteringEnd={(clusterer) => {
clusterer.clusters.map((cluster) => {
cluster.clusterIcon.styles = makeDynamicClusterIcon(cluster.markers)
})
}}
And I changed the last line with return of the makeDynamicClusterIcon function I showed above to instead say:
return [{ url: newIconURL, height: 60, width: 60, textColor: '#FFFFFF', textSize: 22 }];
This question already has answers here:
how to open a local PDF in PDFJS using file input?
(3 answers)
Closed 3 years ago.
Convert the upload data to base64 and I want to preview it before sending.
Image data convert to base64 and shown in the <img> tag.
Similarly, text file also has done.
But now getting stuff in PDF preview.
Expecting that when I upload, PDF should be a preview.
<div className="text-center">
{this.state.files.map((file, i) => {
if (
file.Image_extension == "html" ||
file.Image_extension == "png" ||
file.Image_extension == "PNG" ||
file.Image_extension == "JPG" ||
file.Image_extension == "jpg"
) {
return <img key={i} src={file.type} />;
console.log(file.type);
} else if (file.Image_extension == "txt" || file.Image_extension == "TXT") {
// alert(file.type)
console.log(<textarea key={i} value={file.type}></textarea>);
return <textarea id="text" key={i} value={file.type}></textarea>;
} else {
return <PDF file={file.type} scale={1.3} pages={Infinity} />;
}
})}
</div>;
You may use mozilla pdf.js. You can check it out here
document.querySelector("#pdf-upload").addEventListener("change", function(e) {
var canvasElement = document.querySelector("canvas")
var file = e.target.files[0]
if (file.type != "application/pdf") {
console.error(file.name, "is not a pdf file.")
return
}
var fileReader = new FileReader();
fileReader.onload = function() {
var typedarray = new Uint8Array(this.result);
PDFJS.getDocument(typedarray).then(function(pdf) {
// you can now use *pdf* here
console.log("the pdf has ", pdf.numPages, "page(s).")
pdf.getPage(pdf.numPages).then(function(page) {
// you can now use *page* here
var viewport = page.getViewport(2.0);
var canvas = document.querySelector("canvas")
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({
canvasContext: canvas.getContext('2d'),
viewport: viewport
});
});
});
};
fileReader.readAsArrayBuffer(file);
})
body {
background: rgb(204, 204, 204);
}
.page {
background: white;
display: block;
margin: 0 auto;
margin-bottom: 0.5cm;
box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
}
.page[size="A4"] {
width: 21cm;
height: 29.7cm;
}
.page[size="A4"][layout="landscape"] {
width: 29.7cm;
height: 21cm;
}
.page[size="A3"] {
width: 29.7cm;
height: 42cm;
}
.page[size="A3"][layout="landscape"] {
width: 42cm;
height: 29.7cm;
}
.page[size="A5"] {
width: 14.8cm;
height: 21cm;
}
.page[size="A5"][layout="landscape"] {
width: 21cm;
height: 14.8cm;
}
#media print {
body,
page {
margin: 0;
box-shadow: 0;
}
}
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/1.8.349/pdf.min.js"></script>
<input type="file" id="pdf-upload" />
<hr/>
<canvas class="page" size="A4" layout="landscape"></canvas>
I'm using some custom attributes while I'm creating my objects. For example in this case "name" and "icon":
$scope.addRoundRect = function () {
var coord = getRandomLeftTop();
var roundrect = (new fabric.Rect({
left: coord.left,
top: coord.top,
fill: '#' + getRandomColor(),
width: 250,
height: 250,
opacity: 1,
scaleX: 1,
scaleY: 1,
angle: 0,
rx: 10,
ry: 10,
strokeWidth: 0,
name: "Rounded Rectangle",
icon: "crop-square"
}));
canvas.add(roundrect).setActiveObject(roundrect);
};
This is my copy/paste function. As you can see I have already tried to paste the relevant attributes – bu I think that they are simply not cloned with the object:
function copy() {
canvas.getActiveObject().clone(function (cloned) {
_clipboard = cloned;
});
}
function paste() {
_clipboard.clone(function (clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
name: clonedObj.name,
icon: clonedObj.icon,
});
if (clonedObj.type === 'activeSelection') {
clonedObj.canvas = canvas;
clonedObj.forEachObject(function (obj) {
canvas.add(obj);
});
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
});
To make it short: is there a way to clone and paste also this attributes without having to modify the source (ie. impleneting a full fledged custom attribute in the JSO serialization)?
var canvas = new fabric.Canvas('c');
var roundrect = new fabric.Rect({
left: 50,
top: 30,
fill: 'blue',
width: 250,
height: 250,
opacity: 1,
scaleX: 1,
scaleY: 1,
angle: 0,
rx: 10,
ry: 10,
strokeWidth: 0,
name: "Rounded Rectangle",
icon: "crop-square"
});
canvas.add(roundrect).setActiveObject(roundrect);
var customProperties = 'name icon'.split(' ');
function copy() {
canvas.getActiveObject().clone(function(cloned) {
console.log(cloned);
_clipboard = cloned;
}, customProperties);
}
function paste() {
// clone again, so you can do multiple copies.
_clipboard.clone(function(clonedObj) {
canvas.discardActiveObject();
clonedObj.set({
left: clonedObj.left + 10,
top: clonedObj.top + 10,
evented: true,
});
if (clonedObj.type === 'activeSelection') {
// active selection needs a reference to the canvas.
clonedObj.canvas = canvas;
clonedObj.forEachObject(function (obj) {
canvas.add(obj);
});
// this should solve the unselectability
clonedObj.setCoords();
} else {
canvas.add(clonedObj);
}
canvas.setActiveObject(clonedObj);
canvas.requestRenderAll();
console.log(clonedObj);
_clipboard = clonedObj;
},customProperties);
}
canvas {
border: blue dotted 2px;
}
<script src="https://rawgit.com/kangax/fabric.js/master/dist/fabric.min.js"></script>
<button onclick='copy()'>copy</button>
<button onclick='paste()'>paste</button><br>
<canvas id="c" width="400" height="400"></canvas>
object.clone accepts callback function and any additional property you want to include as another parameter. You can send your name and icon as properties to include.
And in paste you no need to clone that object if you are doing so, make sure there also send you are including your additional properties.
i'm using chartjs and i've added the plugin for adding text inside this chart. My problem is that when i define text like this:
text: ${sectorsCounter}\ Sectors it didnt show the Sectors under the sectorsCounter. My desired output is like this:
The plugin i use for adding text is:
Chart.pluginService.register({
afterUpdate(chart) {
let helpers;
let centerConfig;
let globalConfig;
let ctx;
let fontStyle;
let fontFamily;
let fontSize;
if (chart.config.options.elements.center) {
helpers = Chart.helpers;
centerConfig = chart.config.options.elements.center;
globalConfig = Chart.defaults.global;
ctx = chart.chart.ctx;
fontStyle = helpers.getValueOrDefault(
centerConfig.fontStyle, globalConfig.defaultFontStyle
);
fontFamily = helpers.getValueOrDefault(
centerConfig.fontFamily, globalConfig.defaultFontFamily
);
if (centerConfig.fontSize) {
fontSize = centerConfig.fontSize;
} else {
ctx.save();
fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
ctx.restore();
}
const newChart = chart;
newChart.center = {
font: helpers.fontString(fontSize, fontStyle, fontFamily),
fillStyle: helpers.getValueOrDefault(
centerConfig.fontColor, globalConfig.defaultFontColor
)
};
}
},
afterDraw(chart) {
if (chart.center) {
const centerConfig = chart.config.options.elements.center;
const ctx = chart.chart.ctx;
ctx.save();
ctx.font = chart.center.font;
ctx.fillStyle = chart.center.fillStyle;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const centerX = (chart.chartArea.left + chart.chartArea.right) / 2;
const centerY = (chart.chartArea.top + chart.chartArea.bottom) / 2;
ctx.fillText(centerConfig.text, centerX, centerY);
ctx.restore();
}
},
});
And when i the call of Doughnut chart is:
<Doughnut
data={sectorsData}
width={250}
height={250}
options={{
legend: {
display: false
},
maintainAspectRatio: false,
responsive: true,
cutoutPercentage: 75,
elements: {
center: {
text: `${sectorsCounter}\ Sectors`,
fontColor: '#000000',
fontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
fontStyle: 'normal',
minFontSize: 25,
maxFontSize: 25,
}
},
Well, I see you haven't found your desired solution yet. SO, here's the solution...
add the following code after const centerY = (chart.chartArea.top + chart.chartArea.bottom)/2
in your plugin
let helpers = Chart.helpers;
let fontSize = helpers.getValueOrDefault(centerConfig.minFontSize, 1);
let text = centerConfig.text.split('\ ');
ctx.fillText(text[0], centerX, centerY - fontSize / 2);
ctx.fillText(text[1], centerX, centerY + fontSize / 2);
See Escape notation:
let stringTemplateA = `-a
a-`;
let stringTemplateB = `-b\r\nb-`;
console.log(stringTemplateA);
console.log(stringTemplateB);