I am trying to add a hover effect to each speech bubble in the image below that link to a different page when clicked on.
Currently, I am using an image map and whenever an area of is hovered over jQuery changes the whole image to be the appropriate image that has the speech bubble filled in. It kind of works but IE flickers every time a hover happens and the website is responsive so the image map does not scale when the screen size is changed. I've tried using https://github.com/stowball/jQuery-rwdImageMaps too but it doesn't seem to work on a mobile device.
Ideally I'd like to be able to have the speech bubbles be separate images that are positioned correctly when scaled so I can manipulate the speech bubbles easier.
Screenshot
You could go with approach which uses scaled coordinates.
Here's the basic idea:
var coordManager = {
"imageBaseHeight":800,
"imageBaseWidth":800,
"imageID":"myImg",
"baseCoordinateActions" = [
{"x":10,"y":10,"h":100,"w":100, "text": "Mousing over first option"}, // the square you want the mouseover to cover for a given action
{"x":200,"y":200,"h":100,"w":100, "text": "Mousing over first option"}
],
"scaledCoordinateActions" : [], // this should contain the baseCoordinateActions with the scaled values
"init" : function(id, baseHeight, baseWidth)
{
var self=this;
this.imageID=id;
this.imageBaseHeight=baseHeight;
this.imageBaseWith=baseWidth;
var img = document.getElementById(id);
img.onresize =function()
{
// regenerate the scaled coordinates based on the difference between the imageBaseHeight and the current height;
// usually you can get the scale by dividing the imageBaseHeight by the actual height
};
image.onmousemove = function(event)
{
// check the mouse location based on scaled coordinates if it's within the scaled coordinates of any of the scaledCoordinateAction items
// display that scaleCoordinateAction item's text using the current eventX and Y, or do it relative to the coordinateACtion X and Y.
// make sure to check if the bubble is already showing before changing the display: property to avoid flicker.
}
}
};
coordManager.init("imageID", 800,800);
Related
I am trying to learn openseadragon zoom functionality, however, not understanding something. I put together a simple example and have few questions:
https://codepen.io/Ivalina/pen/poPbaMv
When page first loads, how can I make sure that when I click zoom out right away, image doesn't get any smaller than it already is. This is confusing, as I would hope it starts at its smallest. (priority)
Another question, I did set the min and max zoom levels, yet when I keep on clicking zoom in, it definitely goes beyond 3 clicks. Same goes for zoom out once zoomed in. Is there a way to limit zoom to specific amount of clicks (preferably 3).
var tileSources = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "//openseadragon.github.io/example-images/duomo/duomo_files/",
Format: "jpg",
TileSize: "256",
Size: {
Width: "13920",
Height: "10200"
}
}
};
var viewer = OpenSeadragon({
id: "seadragonviewer",
prefixUrl: "//openseadragon.github.io/openseadragon/images/",
tileSources: tileSources,
visibilityRatio: 1.0,
constrainDuringPan: true,
minZoomLevel: 0,
maxZoomLevel: 2,
zoomPerClick: 1.5,
gestureSettingsMouse: {
clickToZoom: false,
scrollToZoom: false,
flickEnabled: true
},
gestureSettingsTouch: {
clickToZoom: false
},
visibilityRatio: 1.0,
useCanvas: false
});
This isn't necessarily an answer, per se, but I need more space than a comment, so here goes:
The OSD "zoom" is based on the relationship between the width of the image and the width of the viewer. It is 1 when the image exactly fills the viewer horizontally. If you zoom in enough that the viewer only shows half of the width of the image, the zoom is 2. Zoom in enough that the viewer can only hold a quarter of the width, and the zoom is 4. The maxZoomLevel option is based in those terms.
By default, the furthest you can zoom in is based on the ratio of image pixels to screen pixels specified in the maxZoomPixelRatio option (default 1.1). In other words, if maxZoomPixelRatio is 1, then you can only zoom in enough to see the image pixels 1:1. As you've found, you can override that with maxZoomLevel, but then how close to the actual pixels you can get will depend on the size of your viewer.
At any rate, if what you want is to guarantee three clicks and you don't care about how close you get to the actual pixels, here's a scenario:
If your image naturally fills the viewer on the horizontal dimension, you'll get a starting zoom of 1. If zoomPerClick is set to 2, on the first click your zoom will now be 2. On the second click it'll be 4 (zooms are multiplicative), and on the third zoom it'll be 8. Therefore you should set your maxZoomLevel to 8 in that scenario.
If your image doesn't start with a zoom of 1 (because it's tall enough it doesn't fill out the width of the viewer), you'll have to base the maxZoomLevel on that starting zoom. If you have a different zoomPerClick, you'll have to take that into account.
If you do care about how close to the actual pixels you can get on the third click, you'll need to adjust your zoomPerClick so it's different for every viewer size/image size ratio.
I hope this additional background info helps!
EDIT: here's how you might set the zoomPerClick:
What you need to know is the home zoom (where the user starts with the image zoomed all the way out) and the full zoom (when you're zoomed all the way in to see the pixels of the image 1:1), and then you need to figure out what you have to multiply the home zoom by 3 times (since you want 3 clicks) in order to get to the full zoom. Fortunately you can do this after the image is loaded, so OpenSeadragon can do most of the math for you.
viewer.addHandler('open', function() {
const homeZoom = viewer.viewport.getHomeZoom();
const fullZoom = viewer.viewport.imageToViewportZoom(1);
viewer.zoomPerClick = Math.cbrt(fullZoom / homeZoom);
});
I haven't tested this code, but it should be the basic idea.
I am developing a VueJS project and have created a set of cards appearing on the page, when one of these cards is selected, I wish for it to move to centre screen but keep the position it has moved from in the list of options.
I know that by changing the position from 'unset' to 'relative' the card now has move functionality with 'left', 'top' etc. but I still need to find a way to automatically move the card to centre screen regardless of where on the screen the card is moving from.
Does anyone know how to achieve this with the use of JS?
I imagine there is a way of receiving the current location of the node and moving it to the center of the screen, but I am not sure on the specifics of how to achieve it...
Image for context:
CardsProject
EDIT: I have for now gone with rendering an absolute position for the card which means there's no CSS transition from the card's original place to the centre of the screen and the card also temporarily loses its place within the deck.
Before click: click here for image
After click: click here for image
I found the answer after many, many hours of scouring the internet and deepfrying my code.
The answer: Don't use 'relative' positioning!
There's a far nice option to hold the position the element is moving from, but allow for the item to move freely with the use of CSS' top or left etc. and this option is position:sticky;!
With this and the use of JavaScript's coordinates documentation
.getBoundingClientRect()
...I managed to solve the mystery. The function I made to pull a vector between the current object and it the centre of the screen can be found here, returning an array of size 2 of X and Y vectors respectively.
function calcCenterMove(element){
/*
X and Y are the current position of the element to be moved (top left corner).
Width and Height are the width and height of the element to be moved.
CX and CY are the X and Y coordinates of the centre of the screen.
*/
var x = element.getBoundingClientRect().x;
var y = element.getBoundingClientRect().y;
var width = element.getBoundingClientRect().width;
var height = element.getBoundingClientRect().height;
var cx = window.innerWidth / 2;
var cy = window.innerHeight / 2;
var xVector = cx-(width/2)-x;
var yVector = cy-(height/2)-y;
return [xVector, yVector];
}
var xAxisMove = calcCenterMove(element)[0];
var yAxisMove = calcCenterMove(element)[1];
element.style = "transform: translate("+xAxisMove+"px,"+yAxisMove+"px);";
I have paired the above code with a z-index to place the element above all others, and a screen dimming cover, to prevent the user from scrolling elsewhere or interacting with any other options.
Issues still arise here if the user resizes the screen, but I believe that is a different issue to address, possibly by using an event listener to assess a window resize and translate the element from the previous centre to the new centre using the same cx and cy properties above (or perhaps even the entire function!).
Nevertheless, I have come to the answer I was looking for, anyone feel free to use the code above, if needed!
Here are images for reference:
Before click
After click
Regards!
I'm new to canvas but I've been playing with fillRect() to fill a pixel in a certain colour. So far this has worked great, however sometimes the whole canvas blurs and a solid pixel starts to become anti aliased. This is usually caused by the window being resized.
Is there to a way manually assign a pixel a certain colour?
Is there to a way manually assign a pixel a certain colour?
The following show another method of directly accessing a pixel in a canvas
assuming canvas and it's context have already been assigned
and pixel colours have been assigned by, for example
p=new Pixel(35,56,78,0)
setPixel(5,10,p);
function Pixel(r,g,b,a) {
this.red=r;
this.green=g;
this.blue=b;
this.alpha=a;
}
function getPixel(x,y) {
var imdata=context.getImageData(0,0,canvas.width,canvas.height);
var column=4*(y*canvas.width+x);
var pix=new Pixel();
pix.red=imdata.data[column++];
pix.green=imdata.data[column++];
pix.blue=imdata.data[column++];
pix.alpha=imdata.data[column];
return pix;
}
function setPixel(x,y,pix) {
var imdata=context.getImageData(0,0,canvas.width,canvas.height);
var column=4*(y*canvas.width+x);
imdata.data[column++]=pix.red;
imdata.data[column++]=pix.green;
imdata.data[column++]=pix.blue;
imdata.data[column]=pix.alpha;
context.putImageData(imdata,0,0);
}
Alright guys, I'm quite a newbie when it comes to programming however I have managed to get a simple script done to do part of the work.
I have 1,500 images of brake pads that need to be resized to a 1:1 scale so that the image is the same size on screen as it is in real life. I have worked out that the size of each image needs to be (real life width of the pad * 2.834)
My current script prompts the user for the width of the brake pad and assigns the size, in pixels, of the pad image however each pad is a different shape with a different sized border around the image like in this example.
What I need is for the user to be able to select from one side of the pad to the other which will return the current width, in pixels, of the pad. I will then be able to divide how big the pad needs to be by the current size of the pad to find a scale factor for the whole image.
Better Solution:
You can pop a new window with the image using scriptUI and then attach an event listener to that image which you can't do to the document itself, unfortunately. Within the callback function add the X coord to the array, if the array length is greater than 2 hide the window and run the process function. This way, your first click sets the first point, second click sets the second point. And third click runs the processing function. If you want to get really fancy you could use right-click to reset the whole thing if you mess up but I'll leave that up to you.
#target photoshop
function processImage(image, partWidth) {
// Your function to resize the image based on the part width
alert(partWidth); // For testing purposes
}
// Save the current unit preferences (optional)
var startRulerUnits = app.preferences.rulerUnits
var startTypeUnits = app.preferences.typeUnits
// Set units to PIXELS
app.preferences.rulerUnits = Units.PIXELS
app.preferences.typeUnits = TypeUnits.PIXELS
var doc = app.activeDocument; // or the next file in your file array if doing multiple
var clicks = [];
var width = 0;
var w = new Window("dialog", "The Image");
var img = w.add("image", undefined, File(doc.fullName));
img.addEventListener("click", function(k) {
if (clicks.length < 2) {
clicks.push(k.clientX);
}
else {
// absolute value so it doesn't matter if
// we click right or left side first
width = Math.abs(clicks[0] - clicks[1]);
w.hide;
processImage(doc, width);
}
});
w.show();
// Reset to previous unit prefs (optional)
app.preferences.rulerUnits = startRulerUnits;
app.preferences.typeUnits = startTypeUnits;
All you need to do is loop through a bunch of images using something like this Photoshop Script - resize images in folder (dialog box) and throw this code into the loop to do this as a batch.
(You may have a problem if the image is larger than the screen size and scaling the image down or adding scrollbars is very difficult and hacky with scriptUI, unfortunately)
Lame Solution:
One way to go about it is to have the user make a selection of the width of the part using the Rectangular Marquee Tool (the square selection box). Then you can access the dimensions of the selection. The selection bounds are stored in an array in the form of [top-leftX, top-leftY, bottom-rightX, bottom-rightY] To get the width of the selection you can subtract the first X value from the second.
Example snippet:
#target photoshop
var bounds = app.activeDocument.selection.bounds;
var selectionWidth = bounds[3] - bounds[0];
alert(selectionWidth);
The only caveat to this is the user has to make the selection BEFORE running the script.
There could be another way to do this using the scriptUI event listeners. The trouble is they have to be applied to a scriptUI object like a window or a pallate. You could try to make a large window with an opacity of 0 and add a click event to the window to capture the mouse coordinates. Honestly, this would be easier to do outside photoshop using java or C# or something.
I'm building a canvas user interface with jquery and fabric.js library and I set an overlay png image with a transparent section using the following code
var bgImgSrc = bgImg.attr('src');
canvas.setOverlayImage(bgImgSrc, function(img){
canvas.renderAll();
});
I also added an image behind the overlay and resized to fit the container using the following code
var photoImg = $('#img-photo');
var photoImgSrc = photoImg.attr('src');
fabric.Image.fromURL(photoImgSrc, function(img) {
var photoImgWidth = photoImg.width();
var photoImgHeight = photoImg.height();
var hRatio = 380/photoImgWidth;
var vRatio = 300/photoImgHeight;
var ratio = Math.min(hRatio, vRatio);
pImg = img.set({left: 380/2, top: 300/2, angle: 0})
img.scale(ratio).setCoords();
canvas.add(pImg);
canvas.sendToBack(pImg);
canvas.renderAll();
});
And it works as expected, however, when I click on the image to scale/resize it, I don't see the controls, except through the transparent space of the overlay image. Controls are behind the overlay image, is there a way to force show the image controls without having to put the entire image in front of the overlay?
Just set the boolean controlsAboveOverlay:
canvas.controlsAboveOverlay = true;
The problem here is that overlay image is rendered on top of objects' controls. This is expected behavior. Take a look at this wiki article, which shows the z-index of various things on fabric canvas and in which order they're rendered.
There are plans to add support for object controls that are always on top, as you can see from this ticket, but I can't tell you when that's going to happen.
You can also try using "after:render" callback and draw image manually onto canvas.
canvas.controlsAboveOverlay = true; //did the job... but:
I don't want to render controls above the overlay image (as overlay means overlay to me). I just want to render the stack exactly as described in Wiki (by default).
I just wonder...as described in the Wiki rendering-stack, the controls should be rendered on top of all objects (always visible by default), but they do not (look at kitchensink demo).
IMHO this behaviour should be set by a flag like canvas.controlsInStack = true.
By the way ... that thing is really beautiful!