I’m programmatically creating multiple house images that look like this:
I'm doing this by simply iterating through a loop which:
Creates a new Canvas object at each iteration
Draws an SVG of the house onto this new Canvas object
Creates a PNG file from that Canvas
To get some variety going, I’m also programmatically changing the colors of each house at each iteration by simply looking up color-schemes from an Array of color-schemes I created.
All this works great.
But what I’m struggling with is getting my script to AUTOMATICALLY DOWNLOAD each newly created House ".PNG" file to my hard-drive.
I’m trying to do this by creating an ANCHOR <a> tag for each of my canvas/PNG’s and then calling the “.click()” method on each (code is below) - but it’s not working.
Chrome is giving me this error:
And Firefox gives me this error:
Any idea what needs to be done here?
My code is below.
Here's the basic House SVG:
<svg id="HOUSE" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="240.26" height="311.24" viewBox="0 0 240.26 311.24">
<defs>
<style>
.roof-class, .window-class, .door-class {
stroke: #000;
stroke-miterlimit: 10;
}
</style>
</defs>
<g id="House">
<rect class="house-class" x="30.08" y="131.74" width="173.07" height="179"/>
<path d="M270,242V420H98V242H270m1-1H97V421H271V241Z" transform="translate(-67.39 -109.76)"/>
</g>
<polygon id="Roof" class="roof-class" points="1.11 131.74 239.11 131.74 117.11 0.74 1.11 131.74"/>
<rect id="Window2" class="window-class" x="145.11" y="160.74" width="30" height="42"/>
<rect id="Window1" class="window-class" x="58.61" y="160.74" width="30" height="42"/>
<rect id="Door" class="door-class" x="92.11" y="228.74" width="52" height="82"/>
</svg>
Then I have:
window.onload = function() {
alert("window.onload - yo!");
let svgHolder = document.getElementById("HOUSE");
console.log("'svgHolder' = ");
console.log(svgHolder);
// console.log("DIR of 'svgHolder' = " + svgHolder );
svgHolder.onload = function() {
console.log("==> 'svgHolder.onload' --> 'TheHouse' has been loaded!!!");
}
}
// GLOBAL VARIABLES:
const TOTAL_IMAGES = 10;
const canvasWidth = 250;
const canvasHeight = 320;
var canvasX = 0;
var canvasY = 0;
// COLOR-SCHEME VARIABLES:
var colorCounter = 0;
let houseColorSchemesArray = [
{
".house-class": "fuchsia",
".door-class": "darkblue",
".window-class": "yellow",
".roof-class": "maroon"
},
{
".house-class": "gold",
".door-class": "purple",
".window-class": "pink",
".roof-class": "crimson"
},
{
".house-class": "lightblue",
".door-class": "darkslategray",
".window-class": "lightgreen",
".roof-class": "darkred"
} ,
{
".house-class": "blue",
".door-class": "orange",
".window-class": "pink",
".roof-class": "lime"
}
];
// CLASS-NAMES:
let classNamesToPaintArray = [".house-class", ".door-class", ".window-class", ".roof-class"];
// SVG Template:
let houseSVG = document.getElementById("HOUSE");
// var loadedImageCount = 0;
var masterHouseImagesArray = [];
function designOneHouse(theCanvas) {
console.log("= =>>In 'designOneHouse()'!\n");
let context = theCanvas.getContext("2d");
// Now GET-AT and PAINT the Individual SVG Components.
// STRATEGY:
// 1. Iterate through the Array containing all the CLASS-NAMES who's color I want to change.
// 2. For each of these classes, I'll need to iterate through all the HTML elements that are OF that class type
// (there may be like 10 elements that are all styled by the same Style; I want all of them to be updated!)
//
let colorScheme = houseColorSchemesArray[colorCounter];
console.log("==>>Current 'colorScheme' = ");
console.log(colorScheme);
console.log("\n\nNOW Going into a 'forEach' loop!");
classNamesToPaintArray.forEach(className => {
console.log("==>>In 'forEach', current 'className' = " + className);
let elementsArray = houseSVG.querySelectorAll(className);
elementsArray.forEach(element => element.style.fill = colorScheme[className]);
});
var imageData = houseSVG.outerHTML;
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([imageData], { type: 'image/svg+xml;charset=utf-8' });
var url = DOMURL.createObjectURL(svg);
img.onload = function () {
context.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
// Now ADD this new House Image to the 'masterHouseImagesArray':
masterHouseImagesArray.push(img);
console.log("\n >>>'masterHouseImagesArray' now has " + masterHouseImagesArray.length + " images in it." );
if(masterHouseImagesArray.length == TOTAL_IMAGES) {
alert("ALL IMAGES ACCOUNTED FOR!!! \n>Going to make ANCHOR TAGS NOW!!!");
createAnchorTags();
}
}
img.src = url;
// Iterate the ColorCounter - making sure we don't overflow the ColorsArrays:
colorCounter++;
if(colorCounter == houseColorSchemesArray.length) {
colorCounter = 0;
}
console.log("\n\nEXITING 'designOneHouse()'!\n");
}
Finally, I have this:
function makeCanvasGrid() {
console.log("\n\n====>In 'makeCanvasGrid()'!\n");
for(var canvasCounter = 0; canvasCounter < TOTAL_IMAGES; canvasCounter++) {
console.log("\n >FOR LOOP - canvasCounter = " + canvasCounter);
// 1. Create a new Canvas Object:
let newCanvas = document.createElement("canvas");
newCanvas.setAttribute("width", canvasWidth);
newCanvas.setAttribute("height", canvasHeight);
newCanvas.setAttribute("id", "newCanvas" + canvasCounter);
// Log-out just to verify the "id" property was set correctly:
console.log(" >newCanvas.id = " + newCanvas.id);
// 2. Place the Canvas at (x,y) (top, left) coordinates:
newCanvas.style.backgroundColor = "lightblue";
newCanvas.style.position = "absolute";
newCanvas.style.left = canvasX + "px";
newCanvas.style.top = canvasY + "px";
document.body.appendChild(newCanvas);
designOneHouse(newCanvas);
// Check the current Canvas' (X, Y) coords, and if needed, reset X to 0 and SKIP to
// the next "ROW" of Canvasses:
if(canvasCounter > 0 && canvasCounter % 3 == 0) {
console.log(" >>NEXT ROW PLEASE!!!! canvasCount = ", canvasCounter);
canvasX = 0;
canvasY += canvasHeight + 20;
}
else {
canvasX += canvasWidth + 10;
console.log("\n >Increasing 'canvasX' to:" + canvasX);
}
}
}
makeCanvasGrid();
function createAnchorTags() {
console.log("\n\n==========================\n=\n=");
console.log("==>>In 'createAnchorTags()'!");
for(anchorTagsCounter = 0; anchorTagsCounter < TOTAL_IMAGES; anchorTagsCounter++) {
// 1. CREATE a new HTML "a" (anchor) TAG/Element and give it an ID:
let newAnchorTag = document.createElement("a");
newAnchorTag.id = "anchorTag#" + anchorTagsCounter;
// 2. ASSIGN a value to its "href" property:
newAnchorTag.href = masterHouseImagesArray[anchorTagsCounter].src;
console.log("\n >newAnchorTag.href = " + newAnchorTag.href);
// 3. ASSIGN a value to its "download" property:
newAnchorTag.download = "PunkPass#" + anchorTagsCounter;
// 4. APPEND this newly created ANCHOR tag/element to the page:
document.body.appendChild(newAnchorTag);
console.log(" ... ... ....");
console.log(" ->'newAnchorTag' created!");
console.log(" >'newAnchorTag.id' = " + newAnchorTag.id);
newAnchorTag.click();
}
}
Would appreciate any and all help.
Thanks!
If I understand you correctly, you should take a closer look at FileSaver.js - a convenient library for downloading files generated on the client. There is even an example of saving canvas in png file.
Related
I have following JS code (found here, on stackoverflow, and a little-bit modded), which resize image on client side using canvas.
function FileListItem(a) {
// Necesary to proper-work of CatchFile function (especially image-resizing).
// Code by Jimmy Wärting (https://github.com/jimmywarting)
a = [].slice.call(Array.isArray(a) ? a : arguments)
for (var c, b = c = a.length, d = !0; b-- && d;) d = a[b] instanceof File
if (!d) throw new TypeError('expected argument to FileList is File or array of File objects')
for (b = (new ClipboardEvent('')).clipboardData || new DataTransfer; c--;) b.items.add(a[c])
return b.files
}
function CatchFile(obj) {
// Based on ResizeImage function.
// Original code by Jimmy Wärting (https://github.com/jimmywarting)
var file = obj.files[0];
// Check that file is image (regex)
var imageReg = /[\/.](gif|jpg|jpeg|tiff|png|bmp)$/i;
if (!file) return
var uploadButtonsDiv = document.getElementById('upload_buttons_area');
// Check, that it is first uploaded file, or not
// If first, draw a div for showing status
var uploadStatusDiv = document.getElementById('upload_status_area');
if (!uploadStatusDiv) {
var uploadStatusDiv = document.createElement('div');
uploadStatusDiv.setAttribute('class', 'upload-status-area');
uploadStatusDiv.setAttribute('id', 'upload_status_area');
uploadButtonsDiv.parentNode.insertBefore(uploadStatusDiv, uploadButtonsDiv.nextSibling);
// Draw sub-div for each input field
var i;
for (i = 0; i < 3; i++) {
var uploadStatus = document.createElement('div');
uploadStatus.setAttribute('class', 'upload-status');
uploadStatus.setAttribute('id', ('upload_status_id_commentfile_set-' + i + '-file'));
uploadStatusDiv.append(uploadStatus);
}
}
var canvasDiv = document.getElementById('canvas-area');
var currField = document.getElementById(obj.id);
var currFieldLabel = document.getElementById(('label_' + obj.id));
// Main image-converting procedure
if (imageReg.test(file.name)) {
file.image().then(img => {
const canvas = document.createElement('canvas')
canvas.setAttribute('id', ('canvas_' + obj.id));
const ctx = canvas.getContext('2d')
const maxWidth = 1600
const maxHeight = 1200
// Calculate new size
const ratio = Math.min(maxWidth / img.width, maxHeight / img.height)
const width = img.width * ratio + .5|0
const height = img.height * ratio + .5|0
// Resize the canvas to the new dimensions
canvas.width = width
canvas.height = height
// Drawing canvas-object is necessary to proper-work
// on mobile browsers.
// In this case, canvas is inserted to hidden div (display: none)
ctx.drawImage(img, 0, 0, width, height)
canvasDiv.appendChild(canvas)
// Get the binary (aka blob)
canvas.toBlob(blob => {
const resizedFile = new File([blob], file.name, file)
const fileList = new FileListItem(resizedFile)
// Temporary remove event listener since
// assigning a new filelist to the input
// will trigger a new change event...
obj.onchange = null
obj.files = fileList
obj.onchange = CatchFile
}, 'image/jpeg', 0.70)
}
)
// If file is image, during conversion show status
function ShowConvertConfirmation() {
if (document.getElementById('canvas_' + obj.id)) {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' + file.name + ' zakończone!</font>';
return true;
}
else {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Konwertowanie pliku ' + file.name + ' zakończone!</font>';
return false;
}
}
// Loop ShowConvertConfirmation function untill return true (file is converted)
var convertConfirmationLoop = setInterval(function() {
var isConfirmed = ShowConvertConfirmation();
if (!isConfirmed) {
ShowConvertConfirmation();
}
else {
// Break loop
clearInterval(convertConfirmationLoop);
}
}, 2000); // Check every 2000ms
}
// If file is not an image, show status with filename
else {
document.getElementById(('upload_status_' + obj.id)).innerHTML =
'<font color="#4CAF50">Dodano plik ' + file.name + '</font>';
//uploadStatusDiv.append(uploadStatus);
}
}
Canvas is drawn in hidden div:
<div id="canvas-area" style="overflow: hidden; height: 0;"></div>
I am only detect, that div canvas-area is presented and basing on this, JS append another div with status.
Unfortunatelly on some mobile devices (mid-range smartphones), message will be showed before finish of drawing (it is wrong). Due to this, some uploaded images are corrupted or stay in original size.
How to prevent this?
Everything that should happen after the image has loaded, should be executed within the then callback, or called from within it.
It is important to realise that the code that is not within that callback will execute immediately, well before the drawing has completed.
I am trying to work out how to use javascript with photoshop, but eventhough i dont find a logical error in the code, it doesnt work properly.
I have a folder of 1000+ images/.ai files that have varying dimensions. I need these images on the Pillow and saved as .jpeg.
I choose the smartlayer and run the script to choose the images and it saves them correctly. The only problem is, that the resizing of images and positioning dont work properly.
If i put the image in manually, it works without issues, but not with the script.
If the width is greater than the height, it should set the width to 1200 px and calculate the height according to that. (and vice versa) and place in the middle of the layer.
How do i fix the resizing and positioning?
Is it possible to choose a folder where the images are inside instead of selecting the images?
How do i handle it when there are 2 smart layers to change in the mockup instead of 1?
Anyone know where the problem lies this code?
Im grateful for any bit of help!
// Replace SmartObject’s Content and Save as JPG
// 2017, use it at your own risk
// Via #Circle B: https://graphicdesign.stackexchange.com/questions/92796/replacing-a-smart-object-in-bulk-with-photoshops-variable-data-or-scripts/93359
// JPG code from here: https://forums.adobe.com/thread/737789
#target photoshop
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
var theName = myDocument.name.match(/(.*)\.[^\.]+$/)[1];
var thePath = myDocument.path;
var theLayer = myDocument.activeLayer;
// JPG Options;
jpgSaveOptions = new JPEGSaveOptions();
jpgSaveOptions.embedColorProfile = true;
jpgSaveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
jpgSaveOptions.matte = MatteType.NONE;
jpgSaveOptions.quality = 8;
// Check if layer is SmartObject;
if (theLayer.kind != "LayerKind.SMARTOBJECT") {
alert("selected layer is not a smart object")
} else {
// Select Files;
if ($.os.search(/windows/i) != -1) {
var theFiles = File.openDialog("please select files", "*.psd;*.tif;*.jpg;*.ai", true)
} else {
var theFiles = File.openDialog("please select files", getFiles, true)
};
};
(function (){
var startRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var bounds = activeDocument.activeLayer.bounds;
var height = bounds[3].value - bounds[1].value;
var width = bounds[2].value - bounds[0].value;
if (height > width){
var newSize1 = (100 / width) * 800;
activeDocument.activeLayer.resize(newSize1, newSize1, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
}
else{
var newSize2 = (100 / height) * 800;
activeDocument.activeLayer.resize(newSize2, newSize2, AnchorPosition.MIDDLECENTER);
app.preferences.rulerUnits = startRulerUnits;
}
})();
if (theFiles) {
for (var m = 0; m < theFiles.length; m++) {
// Replace SmartObject
theLayer = replaceContents(theFiles[m], theLayer);
var theNewName = theFiles[m].name.match(/(.*)\.[^\.]+$/)[1];
// Save JPG
myDocument.saveAs((new File(thePath + "/" + theName + "_" + theNewName + ".jpg")), jpgSaveOptions, true,Extension.LOWERCASE);
}
}
};
// Get PSDs, TIFs and JPGs from files
function getFiles(theFile) {
if (theFile.name.match(/\.(psd|tif|jpg)$/i) != null || theFile.constructor.name == "Folder") {
return true
}
};
// Replace SmartObject Contents
function replaceContents(newFile, theSO) {
app.activeDocument.activeLayer = theSO;
// =======================================================
var idplacedLayerReplaceContents = stringIDToTypeID("placedLayerReplaceContents");
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc3.putPath(idnull, new File(newFile));
var idPgNm = charIDToTypeID("PgNm");
desc3.putInteger(idPgNm, 1);
executeAction(idplacedLayerReplaceContents, desc3, DialogModes.NO);
return app.activeDocument.activeLayer
};
I have attached 2 pictures. 1 how it needs to look like and 2 what the script outputs
Correct
Wrong
Your replaced images have to be the same resolution as the smart object.
You can declare the folder path in your code. If you still want to select the path by hand, you can select one image in the path, and extract the parent folder path.
You can recursively go through all of the layers in the documents and extract all the smart objects that you want to replace.
You may want a function to recursively traverse all the layers in the document
function browseLayer(layer, fn) {
if (layer.length) {
for (var i = 0; i < layer.length; ++i) {
browseLayer(layer[i], fn)
}
return;
}
if (layer.layers) {
for (var j = 0; j < layer.layers.length; ++j) {
browseLayer(layer.layers[j], fn);
}
return;
}
//apply this function for every layer
fn(layer)
}
Get all the smart objects in the document
const smartObjects = [];
//The smart objects can be visit twice or more
//use this object to mark the visiting status
const docNameIndex = {};
const doc = app.open(new File("/path/to/psd/file"));
browseLayer(doc.layers, function (layer) {
//You cannot replace smart object with position is locked
if (layer.kind == LayerKind.SMARTOBJECT && layer.positionLocked == false) {
smartLayers.push(layer);
doc.activeLayer = layer;
//open the smart object
executeAction(stringIDToTypeID("placedLayerEditContents"), new ActionDescriptor(), DialogModes.NO);
//activeDocument is now the smart object
const docName = app.activeDocument.name;
if (!docNameIndex[docName]) {
docNameIndex[docName] = true;
smartObjects.push({
id: layer.id,
name: layer.name,
docName: docName,
width : app.activeDocument.width.as('pixels'),
height : app.activeDocument.height.as('pixels'),
resolution : app.activeDocument.resolution //important
});
}
//reactive the main document
app.activeDocument = doc;
}
});
I assume that you have two smart objects needed to replace, the images for replacement are stored in different folders with the same name.
smartObjects[0].replaceFolderPath = "/path/to/folder/1";
smartObjects[1].replaceFolderPath = "/path/to/folder/2";
//we need temp folder to store the resize images
smartObjects[0].tempFolderPath = "/path/to/temp/folder/1";
smartObjects[1].tempFolderPath = "/path/to/temp/folder/2";
Ex: The first iteration will replace smartObjects[0] with "/path/to/folder/1/image1.jpg", and smartObjects[1] with "/path/to/folder/image1.jpg"
Now resize all the images following the properties of the smart objects
smartObjects.forEach(function(smartObject){
//Get all files in the folder
var files = new Folder(smartObject.replaceFolderPath).getFiles();
//Resize all the image files
files.forEach(function (file) {
var doc = app.open(file);
doc.resizeImage(
new UnitValue(smartObject.width + ' pixels'),
new UnitValue(smartObject.height + ' pixels'),
smartObject.resolution
);
//save to temp folder
doc.saveAs(
new File(smartObject.tempFolderPath + "/" + file.name),
new PNGSaveOptions(),
true
);
doc.close(SaveOptions.DONOTSAVECHANGES)
});
});
Finally, replace the smart object
//get list of file again
var files = new Folder(smartObject.replaceFolderPath).getFiles();
files.forEach(function(file){
var fileName = file.name;
smartObjects.forEach(function(smartObject){
//active the window opening the smart object
app.activeDocument = app.documents.getByName(args.documentName);
var desc = new ActionDescriptor();
desc.putPath(charIDToTypeID("null"), new File(smartObject.tempFolderPath + "/" + fileName));
executeAction(stringIDToTypeID( "placedLayerReplaceContents" ), desc, DialogModes.NO);
});
//Now export document
var webOptions = new ExportOptionsSaveForWeb();
webOptions.format = SaveDocumentType.PNG; // SaveDocumentType.JPEG
webOptions.optimized = true;
webOptions.quality = 100;
doc.exportDocument(new File("/path/to/result/folder" + file.name), ExportType.SAVEFORWEB, webOptions);
});
Now you can close all the opening smart objects
smartObjects.forEach(function (s) {
app.documents.getByName(r.docName).close(SaveOptions.DONOTSAVECHANGES);
});
I have added images from gallery/camera to an ion-slider view.I have an action sheet which gives me an option to add or remove the images from the slider view.Adding is working fine as i am adding that image to array and providing that updated array to the slider.
But for removing an image i need to get the index of the image that is active on the slider and after that i need to remove it.
I m not able to get the active index using this code :
onSlideChanged() {
try{
let currentIndex = this.slides.getActiveIndex();
console.log("Current index is", currentIndex);
}catch(e){
console.log("not able to get the active index");
} }
this also goes into the catch part.
Then i m removing the first element from the array and updating the array for slider.The array size is reduced when i press the delete button from action sheet but all the images in the slider disappears leaving a blank space.
<div *ngIf="field.value" (click)="takePicture(field.name)">
<ion-slides pager="true" style="max-height:500px" (ionSlideDidChange)="onSlideChanged()">
<ion-slide *ngFor="let c of field.value">
<img class="image-attach" [src]="c" draggable="true">
</ion-slide>
</ion-slides>
</div>
<canvas #latlon style="display:none"></canvas>
</div>
code for setting images to the slider
removePicture(name) {
for (let i = 0; i < this.formType.template.secs.length; i++) {
for (let d = 0; d < this.formType.template.secs[i].fields.length; d++) {
if (this.formType.template.secs[i].fields[d].name == name) {
if(this.formType.template.secs[i].fields[d].value.length>1)
{
this.formType.template.secs[i].fields[d].value.splice(this.currentIndex,1);
this.attachedImagesArray.splice(this.currentIndex,1);
console.log(this.formType.template.secs[i].fields[d].value.length);
var arr = this.formType.template.secs[i].fields[d].value;
console.log("^^^^^^^",arr);
this.formType.template.secs[i].fields[d].value = this.formType.template.secs[i].fields[d].value;
}
else{
console.log("*********",this.formType.template.secs[i].fields[d].value);
this.formType.template.secs[i].fields[d].value = "";
}
}
}
}
};
this is the code i m using for removing the images from slider.
I m new to ionic so don't know why this is happening, i googled for it but nothing worked for me.
for putting the images to the slides i m using the following code :
selecPicture(name) {
this.camera.getPicture({
quality: 50,
destinationType: this.camera.DestinationType.FILE_URI,
targetHeight: 500,
targetWidth: 500,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY
}).then((imageData) => {
//alert(imageData);
// imageData is a base64 encoded string DATA_URL FILE_URI 'lat: '+lat+', lon: '+lon,
let base64Image = "data:image/jpeg;base64," + imageData;
this.geolocation.getCurrentPosition().then((resp) => {
let lat = resp.coords.latitude;
let lon = resp.coords.longitude;
this.lat = lat;
this.lon = lon;
let d = new Date();
let draftSavedTime = d.toString().substr(4, 11);
let canvas = this.canvasRef.nativeElement;
let context = canvas.getContext('2d');
console.log(context);
console.log("hello");
let newImg = new Image();
newImg.src = imageData;
newImg.onload = () => {
canvas.setAttribute("width", newImg.height);
canvas.setAttribute("height", newImg.width);
// context.drawImage(newImg, 0, 0);
context.drawImage(newImg, 0, 0, newImg.width, newImg.height);
// context.font = "15px impact";
context.font = "bold 13px Arial";
// context.textAlign = 'center';
context.fillStyle = 'red';
context.fillText(draftSavedTime, 20, 20);
// context.fillText('Lat: '+lat+' Lon: '+lon, canvas.width / 2, canvas.height * 0.9);
context.fillText('Lat: ' + lat, canvas.width * 0.1, canvas.height * 0.8);
context.fillText('Lon: ' + lon, canvas.width * 0.1, canvas.height * 0.9);
// context.fillText('Lon: '+lon, canvas.width / 2, canvas.height * 0.8);
let image = canvas.toDataURL();
this.attachedImages.push(image);
this.setValue(name, image);
};
}).catch((error) => {
console.log('Error getting location', error);
});
}, (err) => {
console.log(err);
});
}
to set the value of image in the field :
//to set the value in the field
setValue(name, data) {
console.log(this.attachedImages);
console.log(this.formType.template.secs); //3
for (let i = 0; i < this.formType.template.secs.length; i++) {
console.log(this.formType.template.secs[i].fields); //2
for (let d = 0; d < this.formType.template.secs[i].fields.length; d++) {
if (this.formType.template.secs[i].fields[d].name == name) {
this.attachedImagesArray.push({
name : this.formType.template.secs[i].fields[d].name,
image : data,
number : d
});
var group_to_values = this.attachedImagesArray.reduce(function (obj, item) {
obj[item.name] = obj[item.name] || [];
obj[item.name].push(item.image);
return obj;
}, {});
var imageGroup = Object.keys(group_to_values).map(function (key) {
return {key: key, image: group_to_values[key]};
});
var pre = document.createElement("pre");
pre.innerHTML = "imageGroup:\n\n" + JSON.stringify(imageGroup, null, 4);
document.body.appendChild(pre);
var newArr = [];
newArr.push({imageGroup});
for(var item in newArr)
{
console.log(newArr[item]);
for(var sec in newArr[item])
{
console.log(newArr[item][sec]);
}
for( var elm in newArr[item][sec])
{
console.log(newArr[item][sec][elm]);
}
for( var key in newArr[item][sec][elm])
{
this.arrNameStr = newArr[item][sec][elm].key;
console.log(newArr[item][sec][elm].image);
}
}
console.log(this.arrNameStr);
if(this.arrNameStr.includes(this.formType.template.secs[i].fields[d].name))
{
console.log(newArr[item][sec][elm].image);
console.log(this.formType.template.secs[i].fields[d].value);
this.formType.template.secs[i].fields[d].value = newArr[item][sec][elm].image;
console.log(this.formType.template.secs[i].fields[d].value);
}
}
}
}
};
First ensure that you get this event firing off and that your reference to the slides element in the template returns the Slides: (ionSlideDidChange). Do:
onSlideChanged() {
console.log(this.slides)
}
Your console should return reference to the slides element.
It is unclear how you obtain this reference based on your code, but seeing you have *ngIf directive there is a chance you are not actually able to get it since the element doesn't exist at init stage of the component.
To fix that you can pass reference to ion-slides in the ionSlidesChanged method:
<div *ngIf="field.value" (click)="takePicture(field.name)">
<ion-slides #mySlides (ionSlideDidChange)="onSlideChanged(mySlides); mySlides.update()" pager="true" style="max-height:500px">
<ion-slide *ngFor="let c of field.value">
<img class="image-attach" [src]="c" draggable="true">
</ion-slide>
</ion-slides>
</div>
Now in the actual method you should be able to get the value of currentIndex:
onSlideChanged(mySlides) {
let currentIndex = mySlides..getActiveIndex();
// here do splice or whatever manipulation you need with your data source / array
}
Let me know if this helped.
UPDATE: since there is a need to refresh slides UI after the change to the model it is good to call "update" method: (ionSlideDidChange)="onSlideChanged(mySlides); mySlides.update()".
Template is now updated with this.
I have recently been working on a web based project using canvas on HTML5. The program consists of a 16x16 grid of tiles that have been pseudo-randomly generated. I am relatively new to canvas, but have built this program in several other environments, none of which however compile successfully to a web based language. this is the main code section that is giving me bother:
var A = 8765432352450986;
var B = 8765432352450986;
var M = 2576436549074795;
var X = 1;
var rx = 0;
var ry = 0;
this.image = new Image();
var i = 0;
var ii = 0;
while(i < 16)
{
while(ii < 16)
{
this.image = new Image();
this.image.src = "textures/grass.png";
x = (((A*X)+B)%M)%M;
if((x/2)%1 == 0)
{
this.image.src = "textures/grass.png";
}
if((x/8)%1 == 0)
{
this.image.src = "textures/hill.png";
}
if((x/21)%1 == 0)
{
this.image.src = "textures/trees.png";
}
if((x/24)%1 == 0)
{
this.image.src = "textures/sea.png";
}
if((x/55)%1 == 0)
{
this.image.src = "textures/mountain.png";
}
if((x/78)%1 == 0)
{
this.image.src = "textures/lake.png";
}
if((x/521)%1 == 0)
{
this.image.src = "textures/volcano.png";
}
if((x/1700)%1 == 0)
{
this.image.src = "textures/shrine.png";
}
if((x/1890)%1 == 0)
{
this.image.src = "textures/outpost.png";
}
if((x/1999)%1 == 0)
{
this.image.src = "textures/civ.png";
}
ctx = myGameArea.context;
ctx.drawImage(this.image,rx, ry, 20, 20);
ii ++;
rx += 20;
}
i ++;
rx = 0;
ry += 16;
}
I would like canvas to draw along the lines of this code above, effectively generating a grid like this
pre generated grid image
(please try and ignore the obvious bad tile drawings, I planned on either finding an artist or trying slightly harder on them when I get the game fully working.)
The black square is a separate movable object. I haven't got as far as implementing it in this version, but if you have any suggestions for it please tell me
in the full html file I have now, the canvas renders but none of the background (using the w3schools tutorials, I can make objects render however)
In short: how do I render a background consisting of a 16x16 grid of pseudo-random tiles on an event triggered or on page loaded, using canvas or if that does not work another web based technology
Thank you for your time.
A few problems but the main one is that you need to give an image some time to load before you can draw it to the canvas.
var image = new Image();
image.src = "image.png";
// at this line the image may or may not have loaded.
// If not loaded you can not draw it
To ensure an image has loaded you can add a onload event handler to the image
var image = new Image();
image.src = "image.png";
image.onload = function(){ ctx.drawImage(image,0,0); }
The onload function will be called after all the current code has run.
To load many images you want to know when all have loaded. One way to do this is to count the number of images you are loading, and then use the onload to count the number of images that have loaded. When the loaded count is the same as the loading count you know all have loaded and can then call a function to draw what you want with the images.
// Array of image names
const imageNames = "grass,hill,trees,sea,mountain,lake,volcano,shrine,outpost,civ".split(",");
const images = []; // array of images
const namedImages = {}; // object with named images
// counts of loaded and waiting toload images
var loadedCount = 0;
var imageCount = 0;
// tile sizes
const tileWidth = 20;
const tileHeight = 20;
// NOT SURE WHERE YOU GOT THIS FROM so have left it as you had in your code
// Would normally be from a canvas element via canvasElement.getContext("2d")
var ctx = myGameArea.context;
// seeded random function encapsulated in a singleton
// You can set the seed by passing it as an argument rand(seed) or
// just get the next random by not passing the argument. rand()
const rand = (function(){
const A = 8765432352450986;
const B = 8765432352450986; // This value should not be the same as A?? left as is so you get the same values
const M = 2576436549074795;
var seed = 1;
return (x = seed) => seed = ((A * x) + B) % M;
}());
// function loads an image with name
function addImage(name){
const image = new Image;
image.src = "textures/" + name + ".png";
image.onload = () => {
loadedCount += 1;
if(loadedCount === imageCount){
if(typeof allImagesLoaded === "function"){
allImagesLoaded();
}
}
}
imageCount += 1;
images.push(image);
namedImages[name] = image;
}
imageNames.forEach(addImage); // start loading all the images
// This function draws the tiles
function allImagesLoaded(){ /// function that is called when all the images have been loaded
var i, x, y, image;
for(i = 0; i < 256; i += 1){ // loop 16 by 16 times
ctx.drawImage(
images[Math.floor(rand()) % images.length]; //random function does not guarantee an integer so must floor
(i % 16) * tileWidth, // x position
Math.floor(i / 16) * tileHeight, // y position
tileWidth, tileHeight // width and height
);
}
}
I want to capture whole div as image and save on local for proof.I have searched and read many articles about svg to image or div to image.
I have tried some js library for this.But when i try to capture image from div then some captures only div content and some captures only svg content.
s.jpg , a.jpg
html2canvas(contentDiv, {
onrendered: function(can) {
dirty.appendChild(can);
}
});
// first convert your svg to png
exportInlineSVG(svg, function(data, canvas) {
svg.parentNode.replaceChild(canvas, svg);
// then call html2canvas
html2canvas(contentDiv, {
onrendered: function(can) {
can.id = 'canvas';
clean.appendChild(can);
}
});
})
function exportInlineSVG(svg, receiver, params, quality) {
if (!svg || !svg.nodeName || svg.nodeName !== 'svg') {
console.error('Wrong arguments : should be \n exportSVG(SVGElement, function([dataURL],[canvasElement]) || IMGElement || CanvasElement [, String_toDataURL_Params, Float_Params_quality])')
return;
}
var xlinkNS = "http://www.w3.org/1999/xlink";
var clone;
// This will convert an external image to a dataURL
var toDataURL = function(image) {
var img = new Image();
// CORS workaround, this won't work in IE<11
// If you are sure you don't need it, remove the next line and the double onerror handler
// First try with crossorigin set, it should fire an error if not needed
img.crossOrigin = 'Anonymous';
img.onload = function() {
// we should now be able to draw it without tainting the canvas
var canvas = document.createElement('canvas');
var bbox = image.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
// draw the loaded image
canvas.getContext('2d').drawImage(this, 0, 0, bbox.width, bbox.height);
// set our original <image>'s href attribute to the dataURL of our canvas
image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
// that was the last one
if (++encoded === total) exportDoc()
}
// No CORS set in the response
img.onerror = function() {
// save the src
var oldSrc = this.src;
// there is an other problem
this.onerror = function() {
console.warn('failed to load an image at : ', this.src);
if (--total === encoded && encoded > 0) exportDoc();
}
// remove the crossorigin attribute
this.removeAttribute('crossorigin');
// retry
this.src = '';
this.src = oldSrc;
}
// load our external image into our img
img.src = image.getAttributeNS(xlinkNS, 'href');
}
// The final function that will export our svgNode to our receiver
var exportDoc = function() {
// check if our svgNode has width and height properties set to absolute values
// otherwise, canvas won't be able to draw it
var bbox = svg.getBBox();
// avoid modifying the original one
clone = svg.cloneNode(true);
if (svg.width.baseVal.unitType !== 1) clone.setAttribute('width', bbox.width);
if (svg.height.baseVal.unitType !== 1) clone.setAttribute('height', bbox.height);
parseStyles();
// serialize our node
var svgData = (new XMLSerializer()).serializeToString(clone);
// remember to encode special chars
var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
var svgImg = new Image();
svgImg.onload = function() {
// if we set a canvas as receiver, then use it
// otherwise create a new one
var canvas = (receiver && receiver.nodeName === 'CANVAS') ? receiver : document.createElement('canvas');
// IE11 doesn't set a width on svg images...
canvas.width = this.width || bbox.width;
canvas.height = this.height || bbox.height;
canvas.getContext('2d').drawImage(this, 0, 0, canvas.width, canvas.height);
// try to catch IE
try {
// if we set an <img> as receiver
if (receiver.nodeName === 'IMG') {
// make the img looks like the svg
receiver.setAttribute('style', getSVGStyles(receiver));
receiver.src = canvas.toDataURL(params, quality);
} else {
// make the canvas looks like the canvas
canvas.setAttribute('style', getSVGStyles(canvas));
// a container element
if (receiver.appendChild && receiver !== canvas)
receiver.appendChild(canvas);
// if we set a function
else if (typeof receiver === 'function')
receiver(canvas.toDataURL(params, quality), canvas);
}
} catch (ie) {
console.warn("Your ~browser~ has tainted the canvas.\n The canvas is returned");
if (receiver.nodeName === 'IMG') receiver.parentNode.replaceChild(canvas, receiver);
else receiver(null, canvas);
}
}
svgImg.onerror = function(e) {
if (svg._cleanedNS) {
console.error("Couldn't export svg, please check that the svgElement passed is a valid svg document.");
return;
}
// Some non-standard NameSpaces can cause this issues
// This will remove them all
function cleanNS(el) {
var attr = el.attributes;
for (var i = 0; i < attr.length; i++) {
if (attr[i].name.indexOf(':') > -1) el.removeAttribute(attr[i].name)
}
}
cleanNS(svg);
for (var i = 0; i < svg.children.length; i++)
cleanNS(svg.children[i]);
svg._cleanedNS = true;
// retry the export
exportDoc();
}
svgImg.src = svgURL;
}
// ToDo : find a way to get only usefull rules
var parseStyles = function() {
var styleS = [],i;
// transform the live StyleSheetList to an array to avoid endless loop
for (i = 0; i < document.styleSheets.length; i++)
styleS.push(document.styleSheets[i]);
// Do we have a `<defs>` element already ?
var defs = clone.querySelector('defs') || document.createElementNS('http://www.w3.org/2000/svg', 'defs');
if (!defs.parentNode)
clone.insertBefore(defs, clone.firstElementChild);
// iterate through all document's stylesheets
for (i = 0; i < styleS.length; i++) {
var style = document.createElement('style');
var rules = styleS[i].cssRules,
l = rules.length;
for (var j = 0; j < l; j++)
style.innerHTML += rules[j].cssText + '\n';
defs.appendChild(style);
}
// small hack to avoid border and margins being applied inside the <img>
var s = clone.style;
s.border = s.padding = s.margin = 0;
s.transform = 'initial';
}
var getSVGStyles = function(node) {
var dest = node.cloneNode(true);
svg.parentNode.insertBefore(dest, svg);
var dest_comp = getComputedStyle(dest);
var svg_comp = getComputedStyle(svg);
var mods = "";
for (var i = 0; i < svg_comp.length; i++) {
if (svg_comp[svg_comp[i]] !== dest_comp[svg_comp[i]])
mods += svg_comp[i] + ':' + svg_comp[svg_comp[i]] + ';';
}
svg.parentNode.removeChild(dest);
return mods;
}
var images = svg.querySelectorAll('image'),
total = images.length,
encoded = 0;
// Loop through all our <images> elements
for (var i = 0; i < images.length; i++) {
// check if the image is external
if (images[i].getAttributeNS(xlinkNS, 'href').indexOf('data:image') < 0)
toDataURL(images[i]);
// else increment our counter
else if (++encoded === total) exportDoc()
}
// if there were no <image> element
if (total === 0) exportDoc();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js"></script>
<div id="contentDiv" style="width: 50%;">
<img class="" src="s.jpg" width="75%">
<svg xmlns="http://www.w3.org/2000/svg" id="svg" height="100%">
<defs>
<clipPath id="my-path">
<text id="texty" style="font-weight:bold;" x="60" y="300" font-size="60">test</text>
</clipPath>
</defs>
<image xlink:href="a.jpg" clip-path="url(#my-path)" width="100%" height="100%" id="filler" preserveAspectRatio="none"></image>
</svg>
</div>
<div id="clean">clean:<br></div>
<div id="dirty">dirty :<br></div>
<style type="text/css">
svg {
position: relative;
top: -531px;
left: 120px;
}
</style>
I have attached three images. s.jpg image is my main image which is inside in main div. It is main image where user can write their name with texture color. To write text i have used svg inside main div and for texture i have used a.jpg as hidden image.
I used html2canvas js library to convert div into image but i did not get my desired output. Please help me to find out solution for this problem. Thanx in advance
Try this, very easy to use and works everytime :
Dom-To-Image
you can use saveSvgAsPng.js where you have to pass the svg element id along with file name to the image
in my case
saveSvgAsPng(document.getElementById('canvassvg'), "structure.png");
which stores the image in your temp or browser downloads location
(OR)
if you want to store it on server
convert the innerhtml content of of your main tag , i.e., from svg to /svg to base64 and use the base64 to image converter in the backend
var svgData = new XMLSerializer().serializeToString( svgobj );
var canvas = document.createElement( "canvas" );
var ctx = canvas.getContext( "2d" );
var img = document.createElement( "img" );
function toSolidBytes(match, p1) {
return String.fromCharCode('0x' + p1);
})));
svgData= btoa(encodeURIComponent(svgData).replace(/%([0-9A-F]{2})/g,
function toSolidBytes(match, p1) {
return String.fromCharCode('0x' + p1);
}));
img.setAttribute( "src", "data:image/svg+xml;base64," + svgData );
var canvasd='';
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
canvasd = canvas.toDataURL( "image/svg+xml" );
-base64 data is in canvasd
-ajax call to store to backend
};
return;