So I have this simple image editor where I’m using a canvas to draw in a user-selected image and some texts. That is the user can upload an image and then if they want they can add texts or just change the gradient of the image.
Right now the app works perfectly fine except there is one issue.
How to find the issue? Do the following:
- Upload a random image.
- Choose a color from
Text 2 Shadow Offset X
Color Picker. - Increase the slider beside the
Text 2 Shadow Offset X
Color Picker. - Then increase the slider of
Image Gradient and Opacity
.
The image will have a gradient and take the value of the Text 2 Shadow Offset X
Color Picker instead of Image Gradient and Opacity
Color Picker which has default black color.
This is the code:
const canvasTxt = window.canvasTxt.default; const canvas = document.getElementById('canvas'); const ctx = canvas?.getContext('2d'); const btnDownload = document.querySelector('.btnDownload'); const fileUpload = document.querySelector('.file-upload'); const text1 = document.getElementById('text1'); const textForm1 = document.getElementById('text1-form'); const text2 = document.getElementById('text2'); const textForm2 = document.getElementById('text2-form'); const text2ShadowColor = document.getElementById('text2shadowcolor'); const text2ShadowOffsetY = document.getElementById('text2shadowoffy'); const imageForm = document.getElementById('image-form'); const imageGrad = document.getElementById('gradientcolor'); const imageGradOpacity = document.getElementById('gradientopacity'); $(fileUpload).on('change', function(e) { let imgObj = new Image(); imgObj.onload = draw; imgObj.onerror = failed; imgObj.src = URL.createObjectURL(this.files[0]); imgManipulation( e, imgObj ); }); const imgManipulation = ( e, imgObj ) => { $(textForm1).on('change keyup input', updateCanvas); $(textForm2).on('change keyup input', updateCanvas); $(imageForm).on('change keyup input', updateCanvas); function updateCanvas(e) { e.preventDefault(); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(imgObj, 0, 0); createGradient($(imageGrad).val(), $(imageGradOpacity).val()); // TEXT1 STYLES based on user input canvasTxt.fontSize = 30; canvasTxt.drawText( ctx, $(text1).val(), 0, 0, 200, 200 ); // TEXT2 STYLES canvasTxt.font = 20; canvasTxt.drawText( ctx, $(text2).val(), 20, 20, 200, 200 ); } }; function hexToRgb(hex) { var result = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; function createGradient(hex, alpha) { const r = hexToRgb(hex).r.toString(); const g = hexToRgb(hex).g.toString(); const b = hexToRgb(hex).b.toString(); var gradient = ctx.createLinearGradient(800, 0, 0, 0); gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, ${alpha})`); ctx.save() // <----------- ADD ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore() // <----------- ADD }; function draw() { canvas.width = this.naturalWidth; canvas.height = this.naturalHeight; const nw = this.naturalWidth; const nh = this.naturalHeight; ctx.drawImage(this, 0, 0, nw, nh); }; function failed() { console.error("The provided file couldn't be loaded as an Image media"); }; $(btnDownload).on('click', function(e) { const a = document.createElement('a'); document.body.appendChild(a); a.href = canvas.toDataURL(); a.download = "canvas-image.png"; a.click(); document.body.removeChild(a); });
#canvas{ background-color: transparent; width: 30%; height: auto; border: 1px solid #777; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://unpkg.com/canvas-txt@3.0.0/build/index.js"></script> <canvas id="canvas" width="800" height="500"></canvas> <div> <input type="file" class="file-upload" /> <button class="btnDownload">Download</button> </div> <div> <form id="text1-form"> <input type="text" id="text1" placeholder="text 1"/> </form> </div> <div> <form id="text2-form"> <input type="text" id="text2" placeholder="text 2"/> </form> </div> <div> <h2>Image Gradient and Opacity</h2> <form id="image-form"> <input type="color" id="gradientcolor" value="#000000" /> <input type="range" id="gradientopacity" min="0" max="1" value="0" step="0.05" /> </form> </div> <div> <h2>Text2 Shadow Offset X</h2> <input type="color" id="text2shadowcolor" value="#000000" /> <input type="range" id="text2shadowoffy" min="0" max="40" value="0" /> </div>
Rundown of the code:
1: First I have the fileUpload
event listener. It takes in an image from the user and creates an image object and draws it on the canvas. Then imgManipulation
function is called with the imgObj
and event
as its arguments.
The
imgManipulation
function starts off with theinput
event listeners for the texts. That is whenever there is a change in the input, that is a user writes something,updateCanvas
function is called.The
updateCanvas
function actually draws the texts on the image. I’m using a package calledcanvasTxt
which helps the text to be multiline.The
createGradient
function call insideupdateCanvas
is the image gradient for the image.
I tried moving the createGradient function call below the drawTexts but then the gradient comes to the top of everything. The texts become darker too.
How can I make the gradient take it’s value only instead of shadow color?
Your help would be highly appreciated.
Thanks in advance
Advertisement
Answer
The shadowColor
still work when drawing ‘Gradient’ next time.
Add ctx.save()
before, and ctx.restore()
after.
const canvasTxt = window.canvasTxt.default; const canvas = document.getElementById('canvas'); const ctx = canvas?.getContext('2d'); const btnDownload = document.querySelector('.btnDownload'); const fileUpload = document.querySelector('.file-upload'); const text1 = document.getElementById('text1'); const textForm1 = document.getElementById('text1-form'); const text2 = document.getElementById('text2'); const textForm2 = document.getElementById('text2-form'); const text2ShadowColor = document.getElementById('text2shadowcolor'); const text2ShadowOffsetY = document.getElementById('text2shadowoffy'); const imageForm = document.getElementById('image-form'); const imageGrad = document.getElementById('gradientcolor'); const imageGradOpacity = document.getElementById('gradientopacity'); $(fileUpload).on('change', function(e) { let imgObj = new Image(); imgObj.onload = draw; imgObj.onerror = failed; imgObj.src = URL.createObjectURL(this.files[0]); imgManipulation( e, imgObj ); }); const imgManipulation = ( e, imgObj ) => { $(textForm1).on('change keyup input', updateCanvas); $(textForm2).on('change keyup input', updateCanvas); $(imageForm).on('change keyup input', updateCanvas); function updateCanvas() { e.preventDefault(); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(imgObj, 0, 0); createGradient($(imageGrad).val(), $(imageGradOpacity).val()); // TEXT1 STYLES based on user input canvasTxt.fontSize = 30; canvasTxt.drawText( ctx, $(text1).val(), 0, 0, 0, 0 ); // TEXT2 STYLES ctx.save() // <----------- ADD ctx.shadowColor = $(text2ShadowColor).val(); ctx.shadowOffsetY = $(text2ShadowOffsetY).val(); canvasTxt.font = 20; canvasTxt.drawText( ctx, $(text2).val(), 20, 20, 0, 0 ); ctx.restore() // <----------- ADD } }; function hexToRgb(hex) { var result = /^#?([a-fd]{2})([a-fd]{2})([a-fd]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; function createGradient(hex, alpha) { const r = hexToRgb(hex).r.toString(); const g = hexToRgb(hex).g.toString(); const b = hexToRgb(hex).b.toString(); var gradient = ctx.createLinearGradient(800, 0, 0, 0); gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, ${alpha})`); ctx.save() // <----------- ADD ctx.fillStyle = gradient; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore() // <----------- ADD }; function draw() { canvas.width = this.naturalWidth; canvas.height = this.naturalHeight; const nw = this.naturalWidth; const nh = this.naturalHeight; ctx.drawImage(this, 0, 0, nw, nh); }; function failed() { console.error("The provided file couldn't be loaded as an Image media"); }; $(btnDownload).on('click', function(e) { const a = document.createElement('a'); document.body.appendChild(a); a.href = canvas.toDataURL(); a.download = "canvas-image.png"; a.click(); document.body.removeChild(a); });
#canvas{ background-color: transparent; width: 30%; height: auto; border: 1px solid #777; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://unpkg.com/canvas-txt@3.0.0/build/index.js"></script> <canvas id="canvas" width="800" height="500"></canvas> <div> <input type="file" class="file-upload" /> <button class="btnDownload">Download</button> </div> <div> <form id="text1-form"> <input type="text" id="text1" placeholder="text 1"/> </form> </div> <div> <form id="text2-form"> <input type="text" id="text2" placeholder="text 2"/> </form> </div> <div> <h2>Image Gradient and Opacity</h2> <form id="image-form"> <input type="color" id="gradientcolor" value="#000000" /> <input type="range" id="gradientopacity" min="0" max="1" value="0" step="0.05" /> </form> </div> <div> <h2>Text2 Shadow Offset X</h2> <input type="color" id="text2shadowcolor" value="#000000" /> <input type="range" id="text2shadowoffy" min="0" max="40" value="0" /> </div>