I am trying to generate an image using the Satori library in Node.js.
import { readFile, writeFile } from "node:fs/promises";
import { html } from "satori-html";
import satori from "satori";
import { Resvg } from "#resvg/resvg-js";
const events = [
{
title: "Event title",
host: "Host name",
type: "Online webinar",
date: "26 Jan",
}
];
const base64Image = async (filename) => {
const file = await readFile(filename, "base64");
return `data:image/png;base64,${file}`;
};
const bottomImg = await base64Image("./follow-me.png");
const logoImg = await base64Image("./logo.png");
const template = html(`
<div style="display: flex; flex-wrap: wrap; margin: 2em 4em; width="100%"; height="50%"; background-image: url('${bottomImg}');">
<div style="display: flex; flex-direction: column;">
<h1>Kubernetes Events starting in the next 24 hours:</h1>
${events
.map(
({ title, date, host, type }) =>
`<h2 style="display: flex; word-break: break-all; ">${date} - ${title} | ${host} - ${type} </h2>`
)
.join(" ")}
</div>
<img width="34" height="43" src="${logoImg}" style="position: absolute; bottom: -15px; right: 240px; margin: 0;" />
</div>
`);
const svg = await satori(template, {
width: 1200,
fonts: [
{
name: "VictorMono",
data: await readFile("./VictorMono-Bold.ttf"),
weight: 700,
style: "normal",
},
],
});
const resvg = new Resvg(svg, {
background: "rgba(241,244,249,255)",
});
const pngData = resvg.render();
const pngBuffer = pngData.asPng();
await writeFile("./events.png", pngBuffer);
The problem is when the output image (events.png) gets generated i also want a background image at the bottom of the parent div, as you can see in the code i have specified background-image: url('${bottomImg}'); as an inline style but it is not showing at the events.png image at all.
The image is generated correctly when i use <img src=url('${bottomImg}'); >, but the background image specified using the background-image property on the parent div is not showing up at all.
But that doesn't solve the issue since i am expecting the events.png to have a background image.
const template = html(`
<div style="display: flex; flex-wrap: wrap; margin: 2em 4em; width="100%"; height="50%"; background-image: url('${bottomImg}');">
// ....
<img width="34" height="43" src="${logoImg}" style="position: absolute; bottom: -15px; right: 240px; margin: 0;" />
</div>
`);
I have checked the URL for the image and it is correct. I am using the Resvg library to convert the generated SVG to a PNG image. Can someone help me with why the background image is not showing and how I can fix it?
I tried using the background-image property in the CSS of my HTML template to set the background image for the generated image. I expected the image to be displayed as the background of the generated image, but it wasn't visible at all.
I checked the URL for the image and it was correct. I also made sure that the image format was supported. Despite these checks, the background image still did not show up in the generated image.
Also tried to refer the satori docs and change the height and width, still not able to see the background image. Please help
Related
so I finished a coding bootcamp a little while ago and I'm still pretty novice to Javascript. I'm having issues finding a solution to creating dynamic code. Basically I have an email Icon under every employee on the team and when hovering over the icon I want it to show their email. I can hard code this but we have multiple team pages with a different amount of employees on them.
<div class="member">
<img class="member-img" src="/assets/images/signage/example.png" alt="">
<h5 class="member-details">example</h5>
<img onmouseover="showEmail()" onmouseleave="hideEmail()" class="email-icon" id="emailIcon2" src="/assets/images/email-asset-128-fix.png" alt="">
<h5 class="email-txt" id="emailTxt">example#email.com</h5>
</div>
Specifically on this page I have 3 other of these divs for each team member. I have put both the Icons and Email texts h5s into arrays with the code below.
const allIcons = [];
$('.email-icon').each(function() {
allIcons.push(this);
});
console.log(allIcons);
const allEmails = [];
$('.email-txt').each(function() {
allEmails.push(this);
})
console.log(allEmails);
Being newer to Javascript I'm struggling to figure out what I should do here and I can't find a similar solution to this online. I want it be when I hover over Icon 1 it shows Email 1 and so forth, same goes for onmouseleave I just want to hide the h5 again. My css for the email-text is below.
.email-txt {
color: #474747;
margin: 0;
padding: 3px;
transform: translateY(-260%);
border-style: solid;
border-radius: 5px;
border-color: #474747;
background-color: darkgray;
color: black;
display: none;
}
I've tried this solution Change Color of Icon When Hovering Over Adjacent Text With jQuery
I don't know if I'm just not doing it right or what but can't get it to work.
Feel free to judge my code too, the more advice the better :). Thanks!
Assuming that the email addresses are in an array, all you need to do is generate a new image with its title attribute set to the email address for each array entry:
["1#2.com", "3#4.com", "4#5.com", "5#6.com"].forEach(function(item){
let link = document.createElement("a"); // Create dynamic anchor
link.href = "mailto:" + item; // Set link to go to array item
let img = document.createElement("img"); // Create dynamic image
img.alt = item; // Set the required alt attribute
img.src = "https://illustoon.com/photo/dl/2751.png"; // Set image source
img.title = item; // Set the tooltip for the image to the array item
link.append(img); // Put the image in the anchor
document.body.append(link); // Put the anchor on the page
});
img { width: 30px; }
<p>Hover over each icon to see the email address
NOTES:
Don't store HTML elements in an array - - they are already in the DOM so there's no reason to maintain a second list of them. Just store the data you want to work with in the array.
Don't use headings (<h1>...<h6>) because of how the text is styled by the browser. Headings are to define document structure and are essential for those who use assistive technologies (like screen readers) to browse the web. An <h5> would only ever be used to sub-divide an existing <h4> section. And an <h4> should only be used to sub-divide an <h3> section, and so on.
You are using JQuery in your code. While there's nothing inherently wrong with JQuery, it's widely overused to solve simple coding scenarios. Your situation here is very simple and really doesn't warrant JQuery. Learn JavaScript very well before learning JavaScript libraries and frameworks.
You could use CSS to handle the hovering effect, when possible CSS is preferrable over JS to handle these scenarios:
const employees = [{
email: "member1#email.com",
img: "👮"
}, {
email: "member2#email.com",
img: "👷"
}, {
email: "member3#email.com",
img: "💂"
}, {
email: "member4#email.com",
img: "🕵"
}]
employees.forEach(d => {
const html = ` <div class="member">
<div class="member-img">${d.img} </>
<h5 class="member-details">${d.email.match(/.*(?=#)/)}</h5>
<div class="email-icon">✉️<h5 class="email-txt" id="emailTxt">${d.email}</h5></div>
</div>`
root.innerHTML += html
})
#root {
display: flex;
justify-content: center;
}
.member {
display: flex;
flex-direction: column;
align-items: center;
}
.email-icon {
position: relative;
font-size: 3rem;
cursor: pointer;
}
.email-txt {
display: none;
position: absolute;
top: 0;
left: 0;
transform: translateX(-50%);
}
.email-icon:hover .email-txt {
display: block;
}
<div id="root"></div>
I'm new to react and I was trying to make a simple website with music notes (just some images) and wanted each note to change color when hovering over it. I know I can do this with :hover, but I wanted to try out useState for practice. I finally got the toggle feature (just the feature that made it change color when hovering) to work, but then in the process, the width got messed up. All other parts of css (position, color etc.) are working so I'm a bit confused as to why the width is staying the original width. Here is the code I have currently. The toggle feature is only on note3 right now because that's the note I was playing around with.
The first bit of code is essentially part of my index.js file with the music note I was working on.
const Body = () => {
const [isNote3, setNote3] = useState("true");
const ToggleNote3 = () =>{
setNote3(!isNote3);
}
const [isB1, setB1] = useState("true");
const ToggleB1 = () =>{
setB1(!isB1);
}
return (
<div>
<div className="sheetmusic">
<img className="sheet" src={sheetmusic} />
</div>
<div className="notes">
<div className={isNote3 ? "note3" : "n3"}>
<img onMouseEnter={ToggleNote3 } src={note1} />
</div>
</div>
</div>
)
}
The second snippet is the relevant css that corresponds with note3.
.n3{
filter: invert(100%) sepia(26%) saturate(5791%) hue-rotate(58deg) brightness(116%) contrast(101%);
left: 25%;
position: absolute;
max-width: 8%;
max-height: auto;
top: 30%;
}
.note3 {
position: absolute;
left: 25%;
max-width: 8%;
max-height: auto;
top: 30%;
}
Here is also a picture of the current situation on my website. (the large note is the one that currently toggles). 3
I've tried playing around with it for a bit and just don't seem to know the issue. Any help would be GREATLY appreciated, thanks so much.
From your CSS snippet both classes note3 and n3 have the same value for max-width so I don't see why the width would change. Try using different values.
Edit: In CSS by default img width and height are set to auto. So what you need to do is add img { max-width: 100%; } to confine all your images to the width of the parent container. See https://codesandbox.io/s/relaxed-mcnulty-p72by?file=/src/styles.css
I've got an image on the page (I'll call it the background image) and I've allowed the user to enter some text that is positioned via css over the background image. As well, there are a few other small images that will be automatically position over the background image using css based on the text.
I want to turn what the user setup/created into an actual downloadable image now, essentially "flattening the layers" in photo editing terms.
I'd also ideally like to do this at a very high resolution as the original background image exists in a much larger and higher resolution format than the one the people see when editing.
I'm not sure the best way to do this. I'd be using NodeJS and Lambdas.
One solution I think would be to perhaps have another page exist with the full size background image and have the css reposition and resize everything perfectly and take a screenshot with puppeteer or something, although I don't know if that'll lose the quality of the original image somehow?
Or do I size the overlayed text and images correctly for the background and take screenshots of each of them, somehow add transparency, and then somehow merge the pictures?
Is there a way easier thing I'm missing or some package that can help?
If you create an element which has the full-size image, overlay the users' text and any other required image - both suitably scaled up in size and position, you can save that element on a canvas and then convert that to an image.
Here is some code to give the idea. It's using html2canvas but actually you could just create the canvas and draw the images and write the text to it without needing a library if preferred. (The code runs from my server and on my laptop and on https://codepen.io/rgspaces/pen/RwoNxVQ but does not run in a SO snippet - ??a CORS problem with iframe inside snippet system??).
<head>
<script src="https://ahweb.org.uk/html2canvas.js"></script>
<style>
body {
overflow: hidden;
margin: 0;
padding: 0;
box-sizing: border-box;
--t: 20px; /* distance from top of the user's text in the workspace */
--l: 20px; /* distance from left of the user's text */
--f: 30px; /* fontsize of the user's text in the workspace */
}
#big {
position: absolute;
left: 100vw;
display: none;
width: auto;
height: auto;
}
#workspace {
width: 50vw;
height: auto;
}
#workspace .mainimg {
width: 100%;
height: auto;
}
#workspace .text, #big .text {
position: absolute;
color: white;
}
#workspace .text {
top: var(--t);
left: var(--l);
font-size: var(--f);
}
</style>
</head>
<body>
<div id="big">
<img id="bigimg" src="https://picsum.photos/id/1016/3844/2563.jpg" class="mainimg" crossOrigin = "anonymous">
<div class="text"></div>
</div>
<div id="workspace">
<img src="https://picsum.photos/id/1016/3844/2563.jpg" class="mainimg">
<div class="text">User's text</div>
<!-- other imgs -->
</div>
<p>YOUR IMAGE WILL APPEAR BELOW - RIGHT CLICK TO SAVE IT TO FILE (WINDOWS); PRESS TO SAVE TO PHOTOS (IOS)</p>
<img id="resultimg" style="clear:both;" src=""/>
<script>
function start() {
let big = document.getElementById('big');
let bigImg = document.getElementById('bigimg');
let bigText = document.querySelector('#big .text');
let width = bigimg.width;
let height = bigimg.height;
let workspace = document.getElementById('workspace');
let workspaceText = document.querySelector('#workspace .text');
let props = window.getComputedStyle(workspace, null);
let scaling = width/props.getPropertyValue("width").replace('px','');
bigText.innerHTML = workspaceText.innerHTML;
// just some simple scaling to give an idea for now
bigText.style.fontSize = 'calc(var(--f) * ' + scaling + ')';
bigText.style.top = 'calc(var(--t) * ' + scaling + ')';
bigText.style.left = 'calc(var(--l) * ' + scaling + ')';
big.style.display = 'inline-block';
window.scrollTo(0, 0);
html2canvas(big, {allowTaint: true, useCORS: true, logging: true, width: width, height: height})
.then((canvas) => {
big.style.display = 'none';
document.body.style.overflow = 'visible';
const imgData = canvas.toDataURL('image/png', 1);
const resultImg = document.getElementById('resultimg')
resultImg.src = imgData;
resultImg.style.width = width + 'px';
resultImg.style.height = 'auto';
});
}
window.onload = start;
</script>
</body>
</html>
I'm working with Microsoft Web Expression to create a website and I want a smaller image on mouseover/hover to be replaced with a larger one; similar to thumbnail idea, however looking at my code I just can't find how to implement any of the advises I've looked at here.
HTML part:
<div id="layer11" style="position: absolute; width: 150px; height: 20px; z-index: 1; left: 0px; top: 0px; " class="auto-style17">
<img id="img1" alt="" height="20" onmouseout="FP_swapImgRestore()" onmouseover="FP_swapImg(1,1,/*id*/'img1',/*url*/'s2.png')" src="s1.png" width="150" /></div>
And here are the javascript functions:
function FP_swapImg() {//v1.0
var doc=document,args=arguments,elm,n; doc.$imgSwaps=new Array(); for(n=2; n<args.length;
n+=2) { elm=FP_getObjectByID(args[n]); if(elm) { doc.$imgSwaps[doc.$imgSwaps.length]=elm;
elm.$src=elm.src; elm.src=args[n+1]; } }
}
function FP_swapImgRestore() {//v1.0
var doc=document,i; if(doc.$imgSwaps) { for(i=0;i<doc.$imgSwaps.length;i++) {
var elm=doc.$imgSwaps[i]; if(elm) { elm.src=elm.$src; elm.$src=null; } }
doc.$imgSwaps=null; }
}
And I just can't figure out what I should change so that the swapped image is larger rather than compressed on the existing one.
you have to remove the width and height attributes
see http://jsbin.com/nifuvitoqa/1/edit
I am using the JavaScript InfoVis Toolkit (http://thejit.org/) and am trying to print my expanded space-tree visualization using canvas.toDataURL("image/png"). While this works for my ForceDirected graph -- in the SpaceTree we have our labels in a separate DIV so when I print the image I get a blank graph.
Does anyone know how to print the labels? Any help would be greatly appreciated. I have attached a manual screenshot of the graph and the image we get when printing.
Yes - I did see the question here -- but it doesnt answer my question as we cannot use "Native" labels because we do some on the fly styling.
HTML Code:
<div id="infovis" style="height: 412px;">
<div id="infovis-canviswidget" style="position: relative; width: 800px; height: 412px;">
<canvas id="infovis-canvas" width=800" height="412" style="position: absolute; top: 0px; left: 0px; width: 800px; height: 412px;"></canvas>
<div id="infovis-label" style="overflow: visible; position: absolute; top: 0px; left: 0px; width: 800px; height: 0px;">
-- Labels are in here --
</div>
</div>
</div>
Manual Screenshot
Blank Printed Image
I sort of solved this issue by using html2canvas plugin. Basically, html2canvas will create a new canvas of a div element (with its children) which then you convert to a png image file with myCanvas.toDataURL("image/png"). This image will include your HTML labels. (Beware that html2canvas may not handle properly the labels' CSS properties.)
html2canvas(document.getElementById("diagram-container"), {
onrendered: function(canvas) {
var img = canvas.toDataURL();
document.write('<img src="'+img+'"/>');
}
});
I should have posted this back in October when I found it -- but it slipped my mind. I was able to find an answer to my own question.
First check out the post here HTML 5 Canvas Save Image
Here is how I implemented the solution (get CanvasSaver function code from link above):
function SaveCanvas(canvasName) {
var canvas = document.getElementById(canvasName);
var imgUrl = null;
if (canvas.getContext) {
//Get alternative image URL for spacetree only
if (canvasName.indexOf("tag") == -1) {
imgUrl = $jit.ST.prototype.print.call();
}
var cs = new CanvasSaver('http://joeltrost.com/php/functions/saveme.php');
//cs.saveJPEG(canvas, 'image');
cs.savePNG(canvas, 'image', imgUrl);
}
}
Finally -- code your ASP button to call the SaveCanvas function:
<asp:ImageButton ID="ImageButton1" ImageUrl="Images/save_icon.png" ToolTip="Save Visualization" AlternateText="Save Visualization" OnClientClick="SaveCanvas('tagCloudVis-canvas');return false;" Style="left: 2px; top:3px; position:relative;" runat=server />
I know this thread is old. But, in case anyone is looking for this and do not want to use html2canvas. here is a solution for you guys.
Label: {
type: 'Native'
},
Add the above in your javascript code var st = new $jit.ST({ <here> })
To save it as a image, add the following code.
HTML:
<a onclick="getImage(this, 'filename.png', 'tree-id')">download tree</a>
JS:
function getImage(a, filename, id){
a.link = document.getElementById(id).toDataURL();
a.download = filename;
}
Happy Coding :)