How to make a canvas text clickable/hoverable? - javascript

So I displayed text to the html canvas with c.fillText. I want to make it so that when I click/hover over the displayed text, a box will show up where we can input text. Is this possible?
var canvas = document.querySelector('canvas');
canvas.width = "1290";
canvas.height = "580";
var c = canvas.getContext("2d");
function ques() {
var question = document.getElementById("qInput").value;
//window.alert(question);
var randomColor = Math.floor(Math.random() * 16777215).toString(16);
var fontSize = Math.floor(Math.random() * 45 + 12);
var x = Math.floor(Math.random() * 1200);
var y = Math.floor(Math.random() * 580);
c.fillStyle = "#" + randomColor;
c.font = fontSize + "px Times New Roman";
c.fillText(question, x, y);
}
canvas {
/*
position: absolute;
top: 9%;
left: 2.5%;
*/
border: 1px solid #ddd;
}
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<p class="title">Days in the Sun</p>
<canvas></canvas>
<!--for the search bar-->
<div class="search">
<input type="text" placeholder="Search.." name="search" id="qInput">
<button type="submit" onclick="ques()">
<i class="fa fa-search"></i>
</button>
</div>

If i understand you , you can do it with :
1)Make cursor pointer of the text with CSS.
canvas{
cursor:pointer;
}
2)Use javaScript.
document.getElementByTagName('canvas').addEventListener('click' , myFunction);
And in this function you can use javasript to change the DOM .If you want to be hovered replase "click" with mouseover ,but if you use mouse over you have to make another event on same element with "mouseout"

in such cases uses frameworks like Phaser/PIXI. But if you want make it by native canvas, hm:
You has x/y coordinates of your text(relatively to canvas element)(left and top x/y)
You can get right and bottom coordinates of text by using:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.measureText(text).width/height
now on click or on mousemove(hover analog) events, just compare the coordinates of the cursor(e.clientX, e.clientY) and the frame of the text, do they converge.
here is more descriptions with example, in link below:
HTML Canvas Hover Text

Related

How to zoom an img in javascript and html

I'd like to zoom an img with a range button in javascript. However, I don't know how to control the last value the user inputs.
This is my html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Img Zoom</title>
<link rel="stylesheet" type="text/css" href="./css/style.css" />
</head>
<body>
<div id="divImg">
<img id="img" src="./img/rose.jpg" width="200px" height="200"" />
</div>
<div class="slidecontainer">
<label for="myRangeZ">Zoom</label>
<input type="range" min="1" max="100" value="20" class="slider" id="myRangeZ" onclick="jsZoom()">
</div>
</body>
<script src="./js/script.js">
</script>
</html>
and this my .js
var z, zoom;
function jsZoom() {
z = document.getElementById("myRangeZ").value;
console.log(z);
img = document.getElementById("img");
console.log(img.width);
console.log(img.height);
if (z > 20) {
zoom = 1.5;
}
else {
zoom = 0.5;
}
document.getElementById("img").style.width = (img.width * zoom) + "px";
document.getElementById("img").style.height = (img.height * zoom) + "px";
}
When the user goes from 20% to 100% the img is getting bigger, but if the range goes from 100% to 20% the img is not decreasing and it's still getting bigger. I can solve this issue with 2 bottons ( ) but I want it with a range button.
You read the width every time it changes. So you are basing the width off at that moment in time, not the time when the image was added to the page. You need to store the initial value. You can then alter the zoom code however you like to change the size based on the original dimensions.
var z, zoom;
function jsZoom() {
z = document.getElementById("myRangeZ").value;
img = document.getElementById("img");
if (!img.dataset.width) {
img.dataset.width = img.width;
img.dataset.height = img.height;
}
const width = +img.dataset.width;
const height = +img.dataset.height;
img.style.width = (width * z/100 + width) + "px";
img.style.height = (height * z/100 + height) + "px";
}
jsZoom();
img {
transition: 0.2s;
}
<div id="divImg">
<img id="img" src="https://placekitten.com/300/300" width="200px" height="200"" />
</div>
<div class="slidecontainer">
<label for="myRangeZ">Zoom</label>
<input type="range" min="1" max="100" value="20" class="slider" id="myRangeZ" oninput="jsZoom()">
Here's a simpler way:
Wrap everything in a <form>. You'll have complete control of all form controls within it.
Place <img> in a block level tag. In the example the <img> is inside a <fieldset> which is not only a block level tag, it's also a form control.
Assign the following ruleset to <img>:
img { object-fit: contain; width: 100% }
This CSS ruleset will ensure that the <img> will conform to it's container (ex. <fieldset>) and maintain aspect ratio as well.
Details are commented in example
// Bind input event on <form>
document.forms.UI.oninput = scaleFrame;
function scaleFrame(e) {
// Reference all form controls
const IO = this.elements;
/*
If the user is interacing with <input>...
...get the value of <input> and convert it into a number...
...set <fieldset> CSS transform:scale() property to the value of <input>...
...display value of <input> with <output>
*/
if (e.target.id === 'scale') {
let size = parseFloat(e.target.value);
IO.frame.style.cssText = `transform: scale(${size})`;
IO.view.value = size;
}
}
html {
font: 300 2ch/1 Consolas;
}
#frame {
width: 200px;
height: 200px;
transform-origin: top left;
}
label {
display: flex;
align-items: center;
}
img {
object-fit: contain;
width: 100%;
}
input,
output {
display: inline-block;
}
#scale {
margin-right: 1rem;
cursor: pointer;
}
<form id='UI'>
<label>
<input id='scale' type='range' min='.1' max='2.5' step='any' value='1'>
x<output id='view'>1.0</output>
</label>
<fieldset id='frame'>
<img src="https://i.ibb.co/hZj77BZ/lena01.jpg" alt="Lena Forsén (Söderberg) 1972">
</fieldset>
</form>
The image can become bigger in size after decreasing the slider because it is set to become 1.5 larger than itself every time the slider is set to a value above 20.
To fix this, simply store the original width and height of the image in a variable so the previous resizing events don't affect additional scaling.
var z, zoom;
var originalWidth = document.getElementById("img").width;
var originalHeight = document.getElementById("img").height;
function jsZoom() {
z = document.getElementById("myRangeZ").value;
img = document.getElementById("img");
if (z > 20) {
zoom = 1.5;
}
else {
zoom = 0.5;
}
document.getElementById("img").style.width = (originalWidth * zoom) + "px";
document.getElementById("img").style.height = (originalHeight * zoom) + "px";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Img Zoom</title>
<link rel="stylesheet" type="text/css" href="./css/style.css" />
</head>
<body>
<div id="divImg">
<img id="img" src="https://cdn.commercev3.net/cdn.edmundsroses.com/images/popup/23703.jpg" width="200px" height="200"" />
</div>
<div class="slidecontainer">
<label for="myRangeZ">Zoom</label>
<input type="range" min="1" max="100" value="20" class="slider" id="myRangeZ" onclick="jsZoom()">
</div>
</body>
<script src="./js/script.js">
</script>
</html>
The problem here is that your function is using the width and height from the image, which is obviously being changed by your function. The issue should be solved by saving the initial width and height and referencing those in the last two lines of your function.
These are just "by-the-ways", but I also refactored the if/else statement and moved the width and height console.log statements to the end for the dimensions to be printed after they're updated.
var z, zoom;
var img = document.getElementById("img");
var width = img.width;
var height = img.height;
function jsZoom() {
z = document.getElementById("myRangeZ").value;
console.log(z);
zoom = z > 20 ? 1.5 : 0.5;
img.style.width = (width * zoom) + "px";
console.log(img.width);
img.style.height = (height * zoom) + "px";
console.log(img.height);
}
EDIT: The width and height variables should remain outside the function to prevent them from being updated by it and affecting the zoom as a result.

Making an image spin with css and jQuery

I'm trying to make a roulette wheel spin when clicking a button, but I can't seem to pass the random number generated from my js file to css
JS
function roulette_spin(btn){
var rand = Math.floor(Math.random() * 720) + 540;
$('.roulette_wheel').css('transform','rotate('+rand+'deg)');
}
HTML
<body>
<div class="roulette_wheel" style="z-index: 0;">
<img src="ayy.jpg" width="500px" height="500px" />
<button value="spin" onclick="roulette_spin(this)">SPIN</button>
</div>
Here is a working example with some updates to your code.
Like #H.Figueiredo suggests, I've changed the rotation selector to the img to avoid the rotation of the button.
HTML
<div class="roulette_wheel" style="z-index: 0;">
<img src="https://picsum.photos/200/300" width="500px" height="500px" />
<button class="spin" value="spin">SPIN</button>
</div>
CSS
function roulette_spin(btn){
var rand = Math.floor(Math.random() * 720) + 540;
$('.roulette_wheel img').css('transform','rotate('+rand+'deg)');
}
$('.spin').click(function(){
roulette_spin($(this).siblings('img'));
});
See this fiddle
I'd say the roulette_wheel class is not assigned to the proper element, the image.
Besides setting the rotate transform won't make the wheel spin, but will just change its rotation angle once (and here without any transition).
Below you will find a working example of assigning a transform style in an animation process.
JQuery calls were replaced by vanilla selectors (getElementsByClassName, getElementById) and style object manipulation instead of JQuery's css method.
Also, look into documentation for requestAnimationFrame
(OK I was a little bored here :) )
var force = 0;
var angle = 0;
var inertia = 0.985;
var minForce = 15;
var randForce = 15;
var rouletteElem = document.getElementsByClassName('roulette_wheel')[0];
var scoreElem = document.getElementById('score');
var values = [
0,27,10,25,29,12,8,19,31,18,6,21,33,16,4,
23,35,14,2,0,28,9,26,30,11,7,20,32,17,5,
22,34,15,3,24,36,16,1
].reverse();
function roulette_spin(btn){
// set initial force randomly
force = Math.floor(Math.random() * randForce) + minForce;
requestAnimationFrame(doAnimation);
}
function doAnimation() {
// new angle is previous angle + force modulo 360 (so that it stays between 0 and 360)
angle = (angle + force) % 360;
// decay force according to inertia parameter
force *= inertia;
rouletteElem.style.transform = 'rotate('+angle+'deg)';
// stop animation if force is too low
if (force < 0.05) {
// score roughly estimated
scoreElem.innerHTML = values[Math.floor(((angle / 360) * values.length) - 0.5)];
return;
}
requestAnimationFrame(doAnimation);
}
<body>
<div style="z-index: 0; position: relative">
<img class="roulette_wheel" src="http://pngimg.com/uploads/roulette/roulette_PNG14.png" width="300px" height="300px" />
<div style="position: absolute; top: 0px; left: 150px; background-color: red; width: 1px; height: 40px;"></div>
<button value="spin" onclick="roulette_spin(this)">SPIN</button>
<span id="score"></span>
</div>
Try this:
<html>
<body>
<div class="roulette_wheel" style="z-index: 0;">
<img src="ayy.jpg" width="500px" height="500px" />
<button value="spin" onclick="roulette_spin()">SPIN</button>
</div>
<script>
function roulette_spin(){
var rand = Math.floor(Math.random() * 720) + 540;
document.querySelector('.roulette_wheel').style.transform = 'rotate('+rand+'deg)';
}
</script>
</body>
</html>

How to draw a line connector between two icons displayed in two different div.

I have displayed one Icons in one Div element. I need to draw a connecting line between those Icons. Please note that the Div is a dynamic element.
I have displayed the current Image below.
Expected image is displayed below.
Please guide me how to do that. Thanks in advance.
Here is a small demo that will help you know how to use jsPlumb achieve what you want.
jsPlumb.ready(function() {
jsPlumb.connect({
source:"firstItem",
target:"secondItem",
endpoint:"Dot"
});
});
span{
display:inline-block;
height:50px;
width:100px;
background-color:yellow;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.9.2/jquery-ui.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jsPlumb/1.4.1/jquery.jsPlumb-1.4.1-all-min.js"></script>
<div id="diagramContainer">
<div >
<span id="firstItem">Smiley1</span>
</div>
<div >
<span style="float:right" id="secondItem">Smiley2</span>
</div>
</div>
Hope it helps :)
The concept is to draw a div with absolute position inside of "starting point" and rotate it on an angle between 2 points:
// a,b = jquery results i.e. a=$('#smile1'), b=$('#smile2'),
function dist(a,b) {
let o1 = a.offset(), o2 = b.offset();
return Math.sqrt(Math.pow(o1.top - o2.top,2) + Math.pow(o1.left - o2.left,2));
}
function angle(a, b) {
let ao = a.offset(), bo = b.offset();
dx = ao.left-bo.left, dy = ao.top-bo.top;
return Math.PI + Math.atan2(dy, dx);
}
function drawConnection(a,b) {
$('.line').remove();
let d = dist(a,b);
a.removeClass('first');
let ang = d<10
? 0
: (angle(a,b) * 180)/ Math.PI;
a.append(
$('<div>').addClass('line')
.css({
width: Math.round(d) +'px',
transform: 'rotate(' + Math.round(ang) + 'deg)'
})
);
return ang;
}
CSS for the line should be:
.line {
position: absolute;
transform-origin: top left; // to make it rotate around top-left
border-top: solid 2px blue; // set any color
top: 10px; // center of your "smile"
left: 10px;
}
Here is the working example

Convert angle (of shadow) and distance (from shadow to text) to a text-shadow effect dynamically

I want to dynamically calculate a text-shadow effect based on a given angle and a distance in pixels.
How can I combine those two values (using script, dynamically) into text-shadow effect?
A little math can help you figure it out. Trigonometry.
If you remember from math class
cos(x) = adjacent/hypotenuse
sin(x) = opposite/hypotenuse.
Using this you can figure out what you want. x is your angle (270 in your example). the hypotenuse is your distance (4 in your example). Solve for Adjacent and opposite and you'll get your x and y translates. add in the blur and color of your choice and you get:
$(document).ready(function(){
$("#evalButton").click(function(){
var degrees = $("#degrees").val();
var distance = $("#distance").val();
var blur = $("#blur").val();
var color = $("#color").val();
var radians = degrees * Math.PI / 180;
var xDistance = Math.cos(radians) * distance;
var yDistance = Math.sin(radians) * distance;
$(".sample").css({
"text-shadow": xDistance + "px "+
yDistance + "px "+
blur + "px #"+color
});
});
});
.sample{
padding: 20px;
font-family: sans-serif;
font-size: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label>Degrees: <input type="number" id="degrees" value="270"/></label>
<br>
<label>Distance: <input type="number" id="distance" value="4"/></label>
<br>
<label>Blur: <input type="number" id="blur" value="2"/></label>
<br>
<label>Color: <input type="text" id="color" value="444"/></label>
<br>
<button type="button" id="evalButton">Calculate</button>
<div class="sample"> Text With Shadow </div>
This could easily be changed into a function that takes the inputs are arguments. If you want to measure your angle from a different spot, simply add your offset to the degrees after we pull in the value from the box.

Dynamically place Unicode characters in a div with background image

I have an image that represents a weight-lifting bar:
I want to put the Full Block Unicode character (e.g. representing plates) on both the left and the right sides of the bar.
I'm trying to do this with 3 divs:
Barbell left: plates that go on the left side of the bar, right-justified
Barbell middle: nothing left blank
Barbell right: plates that go on the right side of the bar, left-justified
Here is a professional representation of the divs with a plate:
I thought I could do this with floats and percentages on div widths, but I'm not having any luck.
Here is my most recent attempt on js fiddle.
UPDATE
I got the answer I wanted, but based on the comment, I discovered that using Canvas was the better route.
I was able to achieve a better result with this html:
<div data-role="page" id="p1">
<div data-role="header"><h1>Header Page 1</h1></div>
<div data-role="content">
<div class="bar-canvas">
<canvas id="_barCanvas"></canvas>
</div>
</div>
</div>
<div data-role="footer"><h4>Footer</h4></div>
</div>
and this js:
var WIDTH_FACTOR = .8; //80% of screen size
var HEIGHT_FACTOR = .1; //10% of height size
var WEIGHT_SPACER = 2;
var ctx = $("#_barCanvas")[0].getContext("2d");
ctx.canvas.width = (window.innerWidth * WIDTH_FACTOR);
ctx.canvas.height = (window.innerHeight * HEIGHT_FACTOR);
var bar_width = ctx.canvas.width * .8;
var bar_height = ctx.canvas.height * .1;
var bar_x = (ctx.canvas.width - bar_width)
var bar_y = (ctx.canvas.height * .5)
var plate_stop_width = bar_width * .01;
var plate_stop_height = bar_height * 4;
var plate_stop_y = bar_y - ((plate_stop_height - (bar_y / 2)));
var rubber_plate_height = bar_height * 8;
var rubber_plate_y = (ctx.canvas.height / 2) - (rubber_plate_height/2) + (bar_height/2);
var small_plate_height = plate_stop_height;
var small_plate_y = plate_stop_y;
var left_plate_stop_x = bar_x + (bar_width * .3);
var right_plate_stop_x = bar_x + (bar_width * .7);
//Draw Bar
ctx.fillStyle = "black";
ctx.fillRect (bar_x, bar_y, bar_width, bar_height);
//Draw Plate stop left
ctx.fillStyle = "black";
ctx.fillRect (left_plate_stop_x, plate_stop_y, plate_stop_width, plate_stop_height);
//Draw Plate stop right
ctx.fillStyle = "black";
ctx.fillRect (right_plate_stop_x, plate_stop_y, plate_stop_width, plate_stop_height);
//Draw 45 lb Plates
var plate_width = bar_width * .04;
var current_plate_height = 0;
ctx.fillStyle = 'red';
ctx.fillRect(left_plate_stop_x - plate_width, rubber_plate_y, plate_width, rubber_plate_height);
ctx.fillStyle = 'red';
ctx.fillRect(right_plate_stop_x + plate_stop_width, rubber_plate_y, plate_width, rubber_plate_height);
I did it changing a bit your markup and css.
Demo (tested on Chrome 22 only)
HTML:
<div data-role="page" id="p1">
<div data-role="header"><h1>Header Page 1</h1></div>
<div data-role="content">
<div class="barbell-background">
<div class="barbell-left">█</div>
<div class="barbell-right">█</div>
</div>
</div>
<div data-role="footer"><h4>Footer</h4></div>
</div>
CSS:
.barbell-background
{
font-size:3em;
line-height:1.4em;
height:1.4em;
position:relative;
background-image:url('http://i.stack.imgur.com/ZmFY4.png');
background-repeat: no-repeat;
background-position: center;
}
.barbell-left, .barbell-right
{
position:absolute;
color:red;
}
.barbell-left
{
right:50%;
margin-right:146px;
}
.barbell-right
{
left:50%;
margin-left:145px;
}​
As Joachim Sauer said, it's probably easier and more consistent to just use divs for the red squares...
Another demo
HTML:
<div data-role="page" id="p1">
<div data-role="header"><h1>Header Page 1</h1></div>
<div data-role="content">
<div class="barbell-background">
<div class="barbell-left"></div>
<div class="barbell-right"></div>
</div>
</div>
<div data-role="footer"><h4>Footer</h4></div>
</div>
CSS:
.barbell-background
{
font-size:3em;
line-height:1.3em;
height:1.3em;
position:relative;
background-image:url('http://i.stack.imgur.com/ZmFY4.png');
background-repeat: no-repeat;
background-position: center;
}
.barbell-left, .barbell-right
{
position:absolute;
background:red;
width:0.5em;
height:100%;
}
.barbell-left
{
right:50%;
margin-right:146px;
}
.barbell-right
{
left:50%;
margin-left:144px;
}​
In both demos you'll see that a pixel "wobbles". Knowing the contents of the red square i could try to fix it.

Categories