making changes to svg element on the fly - javascript

I'm new to react, here i have svg element which is not in my code as you can see code i have provided, but it is in console, i want to change those 'height: 569px;width: 800px;' to height: 100%;width: 100%; my problem is, that does not have className and neither and id, so how can i change those values ? the other one which is above it i have changed that using that id.
that svg element have just 'name'
const _zoomConfig = () => {
const z = d3Select(`#graph-id-graph-wrapper`)
.call(d3Zoom() as any)
.on('wheel.zoom', null)
.on('dblclick.zoom', null)
.on('mousedown.zoom', null)
.on('touchstart.zoom', null)
.on('touchmove.zoom', null)
.on('touchend.zoom', null);
};
useEffect(() => {
_zoomConfig();
});
#graph-id-graph-wrapper {
/* margin-left: -100%; */
width: 100%;
height: 100%;
}
console:
i dont know if this is reacts way, i have tried this:
useEffect(() => {
const k = Array.from(document.getElementsByTagName('svg')).map(
(elem) => elem,
);
k.map((elem) => {
if (elem.getAttribute('name') === 'svg-container-graph-id') {
elem.setAttribute('height', '100%');
elem.setAttribute('width', '100%');
}
});
});
it doesnt affect it:
English is not my mother language so there could be mistakes.

You could change the inline style properties instead:
elem.style.width = '100%';
elem.style.height = '100%';
But I guess the graphs dimensions i.e. aspect ratio is also needed for a proper display. So adding a viewBox attribute might be a good idea:
elem.setAttribute('viewBox', '0 0 569 800');
Ideally you'll set these properties on creation/initialization.
See also: A simple way to make D3.js charts responsive

Related

Change style header/nav with Intersection Observer (IO)

Fiddle latest
I started this question with the scroll event approach, but due to the suggestion of using IntersectionObserver which seems much better approach i'm trying to get it to work in that way.
What is the goal:
I would like to change the style (color+background-color) of the header depending on what current div/section is observed by looking for (i'm thinking of?) its class or data that will override the default header style (black on white).
Header styling:
font-color:
Depending on the content (div/section) the default header should be able to change the font-color into only two possible colors:
black
white
background-color:
Depending on the content the background-color could have unlimited colors or be transparent, so would be better to address that separate, these are the probably the most used background-colors:
white (default)
black
no color (transparent)
CSS:
header {
position: fixed;
width: 100%;
top: 0;
line-height: 32px;
padding: 0 15px;
z-index: 5;
color: black; /* default */
background-color: white; /* default */
}
Div/section example with default header no change on content:
<div class="grid-30-span g-100vh">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_default_header.jpg"
class="lazyload"
alt="">
</div>
Div/section example change header on content:
<div class="grid-30-span g-100vh" data-color="white" data-background="darkblue">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_darkblue.jpg"
class="lazyload"
alt="">
</div>
<div class="grid-30-span g-100vh" data-color="white" data-background="black">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_black.jpg"
class="lazyload"
alt="">
</div>
Intersection Observer approach:
var mq = window.matchMedia( "(min-width: 568px)" );
if (mq.matches) {
// Add for mobile reset
document.addEventListener("DOMContentLoaded", function(event) {
// Add document load callback for leaving script in head
const header = document.querySelector('header');
const sections = document.querySelectorAll('div');
const config = {
rootMargin: '0px',
threshold: [0.00, 0.95]
};
const observer = new IntersectionObserver(function (entries, self) {
entries.forEach(entry => {
if (entry.isIntersecting) {
if (entry.intersectionRatio > 0.95) {
header.style.color = entry.target.dataset.color !== undefined ? entry.target.dataset.color : "black";
header.style.background = entry.target.dataset.background !== undefined ? entry.target.dataset.background : "white";
} else {
if (entry.target.getBoundingClientRect().top < 0 ) {
header.style.color = entry.target.dataset.color !== undefined ? entry.target.dataset.color : "black";
header.style.background = entry.target.dataset.background !== undefined ? entry.target.dataset.background : "white";
}
}
}
});
}, config);
sections.forEach(section => {
observer.observe(section);
});
});
}
Instead of listening to scroll event you should have a look at Intersection Observer (IO).
This was designed to solve problems like yours. And it is much more performant than listening to scroll events and then calculating the position yourself.
First, here is a codepen which shows a solution for your problem.
I am not the author of this codepen and I would maybe do some things a bit different but it definitely shows you the basic approach on how to solve your problem.
Things I would change: You can see in the example that if you scoll 99% to a new section, the heading changes even tough the new section is not fully visible.
Now with that out of the way, some explaining on how this works (note, I will not blindly copy-paste from codepen, I will also change const to let, but use whatever is more appropriate for your project.
First, you have to specify the options for IO:
let options = {
rootMargin: '-50px 0px -55%'
}
let observer = new IntersectionObserver(callback, options);
In the example the IO is executing the callback once an element is 50px away from getting into view. I can't recommend some better values from the top of my head but if I would have the time I would try to tweak these parameters to see if I could get better results.
In the codepen they define the callback function inline, I just wrote it that way to make it clearer on what's happening where.
Next step for IO is to define some elements to watch. In your case you should add some class to your divs, like <div class="section">
let entries = document.querySelectorAll('div.section');
entries.forEach(entry => {observer.observe(entry);})
Finally you have to define the callback function:
entries.forEach(entry => {
if (entry.isIntersecting) {
//specify what should happen if an element is coming into view, like defined in the options.
}
});
Edit: As I said this is just an example on how to get you started, it's NOT a finished solution for you to copy paste. In the example based on the ID of the section that get's visible the current element is getting highlighted. You have to change this part so that instead of setting the active class to, for example, third element you set the color and background-color depending on some attribute you set on the Element. I would recommend using data attributes for that.
Edit 2: Of course you can continue using just scroll events, the official Polyfill from W3C uses scroll events to emulate IO for older browsers.it's just that listening for scroll event and calculating position is not performant, especially if there are multiple elements. So if you care about user experience I really recommend using IO. Just wanted to add this answer to show what the modern solution for such a problem would be.
Edit 3: I took my time to create an example based on IO, this should get you started.
Basically I defined two thresholds: One for 20 and one for 90%. If the element is 90% in the viewport then it's save to assume it will cover the header. So I set the class for the header to the element that is 90% in view.
Second threshold is for 20%, here we have to check if the element comes from the top or from the bottom into view. If it's visible 20% from the top then it will overlap with the header.
Adjust these values and adapt the logic as you see.
const sections = document.querySelectorAll('div');
const config = {
rootMargin: '0px',
threshold: [.2, .9]
};
const observer = new IntersectionObserver(function (entries, self) {
entries.forEach(entry => {
if (entry.isIntersecting) {
var headerEl = document.querySelector('header');
if (entry.intersectionRatio > 0.9) {
//intersection ratio bigger than 90%
//-> set header according to target
headerEl.className=entry.target.dataset.header;
} else {
//-> check if element is coming from top or from bottom into view
if (entry.target.getBoundingClientRect().top < 0 ) {
headerEl.className=entry.target.dataset.header;
}
}
}
});
}, config);
sections.forEach(section => {
observer.observe(section);
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.g-100vh {
height: 100vh
}
header {
min-height: 50px;
position: fixed;
background-color: green;
width: 100%;
}
header.white-menu {
color: white;
background-color: black;
}
header.black-menu {
color: black;
background-color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<header>
<p>Header Content </p>
</header>
<div class="grid-30-span g-100vh white-menu" style="background-color:darkblue;" data-header="white-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_darkblue.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>
<div class="grid-30-span g-100vh black-menu" style="background-color:lightgrey;" data-header="black-menu">
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1.414 1'%3E%3C/svg%3E"
data-src="/images/example_lightgrey.jpg"
class="lazyload"
alt="<?php echo $title; ?>">
</div>
I might not understand the question completely, but as for your example - you can solve it by using the mix-blend-mode css property without using javascript at all.
Example:
header {background: white; position: relative; height: 20vh;}
header h1 {
position: fixed;
color: white;
mix-blend-mode: difference;
}
div {height: 100vh; }
<header>
<h1>StudioX, Project Title, Category...</h1>
</header>
<div style="background-color:darkblue;"></div>
<div style="background-color:lightgrey;"></div>
I've encountered the same situation and the solution I implemented is very precise because it doesn't rely on percentages but on real elements' bounding boxes:
class Header {
constructor() {
this.header = document.querySelector("header");
this.span = this.header.querySelector('span');
this.invertedSections = document.querySelectorAll(".invertedSection");
window.addEventListener('resize', () => this.resetObserver());
this.resetObserver();
}
resetObserver() {
if (this.observer) this.observer.disconnect();
const {
top,
height
} = this.span.getBoundingClientRect();
this.observer = new IntersectionObserver(entries => this.observerCallback(entries), {
root: document,
rootMargin: `-${top}px 0px -${window.innerHeight - top - height}px 0px`,
});
this.invertedSections.forEach((el) => this.observer.observe(el));
};
observerCallback(entries) {
let inverted = false;
entries.forEach((entry) => {
if (entry.isIntersecting) inverted = true;
});
if (inverted) this.header.classList.add('inverted');
else this.header.classList.remove('inverted');
};
}
new Header();
header {
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 20px 0;
text-transform: uppercase;
text-align: center;
font-weight: 700;
}
header.inverted {
color: #fff;
}
section {
height: 500px;
}
section.invertedSection {
background-color: #000;
}
<body>
<header>
<span>header</span>
</header>
<main>
<section></section>
<section class="invertedSection"></section>
<section></section>
<section class="invertedSection"></section>
</main>
</body>
What it does is actually quite simple: we can't use IntersectionObserver to know when the header and other elements are crossing (because the root must be a parent of the observed elements), but we can calculate the position and size of the header to add rootMargin to the observer.
Sometimes, the header is taller than its content (because of padding and other stuff) so I calculate the bounding-box of the span in the header (I want it to become white only when this element overlaps a black section).
Because the height of the window can change, I have to reset the IntersectionObserver on window resize.
The root property is set to document here because of iframe restrictions of the snippet (otherwise you can leave this field undefined).
With the rootMargin, I specify in which area I want the observer to look for intersections.
Then I observe every black section. In the callback function, I define if at least one section is overlapping, and if this is true, I add an inverted className to the header.
If we could use values like calc(100vh - 50px) in the rootMargin property, we may not need to use the resize listener.
We could even improve this system by adding side rootMargin, for instance if I have black sections that are only half of the window width and may or may not intersect with the span in the header depending on its horizontal position.
#Quentin D
I searched the internet for something like this, and I found this code to be the best solution for my needs.
Therefore I decided to build on it and create a universal "Observer" class, that can be used in many cases where IntesectionObserver is required, including changing the header styles.
I haven't tested it much, only in a few basic cases, and it worked for me. I haven't tested it on a page that has a horizontal scroll.
Having it this way makes it easy to use it, just save it as a .js file and include/import it in your code, something like a plugin. :)
I hope someone will find it useful.
If someone finds better ideas (especially for "horizontal" sites), it would be nice to see them here.
Edit: I hadn't made the correct "unobserve", so I fixed it.
/* The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
ROOT:
It is not necessary for the root to be the ancestor element of the target. The root is allways the document, and the so-called root element is used only to get its size and position, to create an area in the document, with options.rootMargin.
Leave it false to have the viewport as root.
TARGET:
IntersectionObserver triggers when the target is entering at the specified ratio(s), and when it exits at the same ratio(s).
For more on IntersectionObserverEntry object, see:
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#targeting_an_element_to_be_observed
IntersectionObserverEntry.time // Timestamp when the change occurred
IntersectionObserverEntry.rootBounds // Unclipped area of root
IntersectionObserverEntry.intersectionRatio // Ratio of intersectionRect area to boundingClientRect area
IntersectionObserverEntry.target // the Element target
IntersectionObserverEntry.boundingClientRect // target.boundingClientRect()
IntersectionObserverEntry.intersectionRect // boundingClientRect, clipped by its containing block ancestors, and intersected with rootBounds
THRESHOLD:
Intersection ratio/threshold can be an array, and then it will trigger on each value, when in and when out.
If root element's size, for example, is only 10% of the target element's size, then intersection ratio/threshold can't be set to more than 10% (that is 0.1).
CALLBACKS:
There can be created two functions; when the target is entering and when it's exiting. These functions can do what's required for each event (visible/invisible).
Each function is passed three arguments, the root (html) element, IntersectionObserverEntry object, and intersectionObserver options used for that observer.
Set only root and targets to only have some info in the browser's console.
For more info on IntersectionObserver see: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
Polyfill: <script src="https://polyfill.io/v3/polyfill.js?features=IntersectionObserver"></script>
or:
https://github.com/w3c/IntersectionObserver/tree/main/polyfill
Based on answer by Quentin D, answered Oct 27 '20 at 12:12
https://stackoverflow.com/questions/57834100/change-style-header-nav-with-intersection-observer-io
root - (any selector) - root element, intersection parent (only the first element is selected).
targets - (any selector) - observed elements that trigger function when visible/invisible.
inCb - (function name) - custom callback function to trigger when the target is intersecting.
outCb - (function name) - custom callback function to trigger when the target is not intersecting.
thres - (number 0-1) - threshold to trigger the observer (e.g. 0.1 will trigger when 10% is visible).
unobserve- (bolean) - if true, the target is unobserved after triggering the callback.
EXAMPLE:
(place in 'load' event listener, to have the correct dimensions)
var invertedHeader = new Observer({
root: '.header--main', // don't set to have the viewport as root
targets: '[data-bgd-dark]',
thres: [0, .16],
inCb: someCustomFunction,
});
*/
class Observer {
constructor({
root = false,
targets = false,
inCb = this.isIn,
outCb = this.isOut,
thres = 0,
unobserve = false,
} = {}) {
// this element's position creates with rootMargin the area in the document
// which is used as intersection observer's root area.
// the real root is allways the document.
this.area = document.querySelector(root); // intersection area
this.targets = document.querySelectorAll(targets); // intersection targets
this.inCallback = inCb; // callback when intersecting
this.outCallback = outCb; // callback when not intersecting
this.unobserve = unobserve; // unobserve after intersection
this.margins; // rootMargin for observer
this.windowW = document.documentElement.clientWidth;
this.windowH = document.documentElement.clientHeight;
// intersection is being checked like:
// if (entry.isIntersecting || entry.intersectionRatio >= this.ratio),
// and if ratio is 0, "entry.intersectionRatio >= this.ratio" will be true,
// even for non-intersecting elements, therefore:
this.ratio = thres;
if (Array.isArray(thres)) {
for (var i = 0; i < thres.length; i++) {
if (thres[i] == 0) {
this.ratio[i] = 0.0001;
}
}
} else {
if (thres == 0) {
this.ratio = 0.0001;
}
}
// if root selected use its position to create margins, else no margins (viewport as root)
if (this.area) {
this.iArea = this.area.getBoundingClientRect(); // intersection area
this.margins = `-${this.iArea.top}px -${(this.windowW - this.iArea.right)}px -${(this.windowH - this.iArea.bottom)}px -${this.iArea.left}px`;
} else {
this.margins = '0px';
}
// Keep this last (this.ratio has to be defined before).
// targets are required to create an observer.
if (this.targets) {
window.addEventListener('resize', () => this.resetObserver());
this.resetObserver();
}
}
resetObserver() {
if (this.observer) this.observer.disconnect();
const options = {
root: null, // null for the viewport
rootMargin: this.margins,
threshold: this.ratio,
}
this.observer = new IntersectionObserver(
entries => this.observerCallback(entries, options),
options,
);
this.targets.forEach((target) => this.observer.observe(target));
};
observerCallback(entries, options) {
entries.forEach(entry => {
// "entry.intersectionRatio >= this.ratio" for older browsers
if (entry.isIntersecting || entry.intersectionRatio >= this.ratio) {
// callback when visible
this.inCallback(this.area, entry, options);
// unobserve
if (this.unobserve) {
this.observer.unobserve(entry.target);
}
} else {
// callback when hidden
this.outCallback(this.area, entry, options);
// No unobserve, because all invisible targets will be unobserved automatically
}
});
};
isIn(rootElmnt, targetElmt, options) {
if (!rootElmnt) {
console.log(`IO Root: VIEWPORT`);
} else {
console.log(`IO Root: ${rootElmnt.tagName} class="${rootElmnt.classList}"`);
}
console.log(`IO Target: ${targetElmt.target.tagName} class="${targetElmt.target.classList}" IS IN (${targetElmt.intersectionRatio * 100}%)`);
console.log(`IO Threshold: ${options.threshold}`);
//console.log(targetElmt.rootBounds);
console.log(`============================================`);
}
isOut(rootElmnt, targetElmt, options) {
if (!rootElmnt) {
console.log(`IO Root: VIEWPORT`);
} else {
console.log(`IO Root: ${rootElmnt.tagName} class="${rootElmnt.classList}"`);
}
console.log(`IO Target: ${targetElmt.target.tagName} class="${targetElmt.target.classList}" IS OUT `);
console.log(`============================================`);
}
}
This still needs adjustment, but you could try the following:
const header = document.getElementsByTagName('header')[0];
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
header.style.color = entry.target.dataset.color || '';
header.style.backgroundColor = entry.target.dataset.background;
}
});
}, { threshold: 0.51 });
[...document.getElementsByClassName('observed')].forEach((t) => {
t.dataset.background = t.dataset.background || window.getComputedStyle(t).backgroundColor;
observer.observe(t);
});
body {
font-family: arial;
margin: 0;
}
header {
border-bottom: 1px solid red;
margin: 0 auto;
width: 100vw;
display: flex;
justify-content: center;
position: fixed;
background: transparent;
transition: all 0.5s ease-out;
}
header div {
padding: 0.5rem 1rem;
border: 1px solid red;
margin: -1px -1px -1px 0;
}
.observed {
height: 100vh;
border: 1px solid black;
}
.observed:nth-of-type(2) {
background-color: grey;
}
.observed:nth-of-type(3) {
background-color: white;
}
<header>
<div>One</div>
<div>Two</div>
<div>Three</div>
</header>
<div class="observed">
<img src="http://placekitten.com/g/200/300">
<img src="http://placekitten.com/g/400/300">
</div>
<div class="observed" data-color="white" data-background="black">
<img src="http://placekitten.com/g/600/300">
</div>
<div class="observed" data-color="black" data-background="white">
<img src="http://placekitten.com/g/600/250">
</div>
The CSS ensures each observed section takes up 100vw and the observer does its thing when anyone of those comes into view by at least 51% percent.
In the callback the headers background-color is then set to the background-color of the intersecting element.

How to restart updated animation after dynamically changing keyframes?

I'm trying to create a marquee (yes, I've done LOTS of searching on that topic first) using animated text-indent. I prefer this solution over others I've tried, like using translation 100%, which causes text to leak out beyond the boundaries of my marquee.
I've been trying to follow this example here: https://www.jonathan-petitcolas.com/2013/05/06/simulate-marquee-tag-in-css-and-javascript.html
...which I've updated a bit, doing it in TypeScript, using API updates (appendRule instead of insertRule) and dropping concerns about old browser support.
The problem is that the animation restarts using the old keyframe rules -- the step described by the comment "re-assign the animation (to make it run)" doesn't work.
I've looked at what's going on in a debugger, and the rules are definitely being changed -- old rules deleted, new rules added. But it's as if the old rules are cached somewhere, and they aren't being cleared out.
Here's my current CSS:
#marquee {
position: fixed;
left: 0;
right: 170px;
bottom: 0;
background-color: midnightblue;
font-size: 14px;
padding: 2px 1em;
overflow: hidden;
white-space: nowrap;
animation: none;
}
#marquee:hover {
animation-play-state: paused;
}
#keyframes marquee-0 {
0% {
text-indent: 450px;
}
100% {
text-indent: -500px;
}
}
And the relevant section of my TypeScript:
function updateMarqueeAnimation() {
const marqueeRule = getKeyframesRule('marquee-0');
if (!marqueeRule)
return;
marquee.css('animation', 'unset');
const element = marquee[0];
const textWidth = getTextWidth(marquee.text(), element);
const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
const offsetWidth = element.offsetWidth;
if (textWidth + padding <= offsetWidth)
return;
marqueeRule.deleteRule('0%');
marqueeRule.deleteRule('100%');
marqueeRule.appendRule('0% { text-indent: ' + offsetWidth + 'px; }');
marqueeRule.appendRule('100% { text-indent: -' + textWidth + 'px; }');
setTimeout(() => marquee.css('animation', 'marquee-0 15s linear infinite'));
}
I've tried a number of tricks so far to get around this problem, including things like cloning the marquee element and replacing it with its own clone, and none of that has helped -- the animation continues to run as if the original stylesheet values are in effect, so the scrolling of the marquee doesn't adapt to different widths of text.
The next thing I'll probably try is dynamically creating new keyframes objects instead of editing the rules inside of an existing keyframes object, but that's a messy solution I'd rather avoid if anyone has a better solution.
I found a way to get my marquee working, and it did involved dynamically adding and removing keyframes rules from a stylesheet, but that wasn't as painful or ugly as I thought it might be.
let animationStyleSheet: CSSStyleSheet;
let keyframesIndex = 0;
let lastMarqueeText = '';
function updateMarqueeAnimation(event?: Event) {
const newText = marquee.text();
if (event === null && lastMarqueeText === newText)
return;
lastMarqueeText = newText;
marquee.css('animation', 'none');
const element = marquee[0];
const textWidth = getTextWidth(newText, element);
const padding = Number(window.getComputedStyle(element).getPropertyValue('padding-left').replace('px', '')) +
Number(window.getComputedStyle(element).getPropertyValue('padding-right').replace('px', ''));
const offsetWidth = element.offsetWidth;
if (textWidth + padding <= offsetWidth)
return;
if (!animationStyleSheet) {
$('head').append('<style id="marquee-animations" type="text/css"></style>');
animationStyleSheet = ($('#marquee-animations').get(0) as HTMLStyleElement).sheet as CSSStyleSheet;
}
if (animationStyleSheet.cssRules.length > 0)
animationStyleSheet.deleteRule(0);
const keyframesName = 'marquee-' + keyframesIndex++;
const keyframesRule = `#keyframes ${keyframesName} { 0% { text-indent: ${offsetWidth}px } 100% { text-indent: -${textWidth}px; } }`;
const seconds = (textWidth + offsetWidth) / 100;
animationStyleSheet.insertRule(keyframesRule, 0);
marquee.css('animation', `${keyframesName} ${seconds}s linear infinite`);
}
There's other stuff going on here not needed for a general solution. One thing is that this method is called for two reasons: The window is being resized, or an update to the marquee text has been made. I always want to update when the window is resized, but otherwise I don't want to update the animation if the text hasn't changed, otherwise it could unnecessarily reset when someone is trying to read it.
The other thing is that I don't want text to scroll at all if it happens to fit nicely without scrolling.

Zoom inside of a div, changing the inner elements but not the div itself

I am looking for a way to zoom into a div element. Since this question did not provide some code example, I decided to post the following.
This jsfiddle already helped, but as you can see in my adapted jsfiddle, the whole div scales. I just want the image inside to scale. the div should have an overflow if zoomed in (also scroll bars).
Is this possible without including other scripts e.g. panzoom?
HTML
<div id="pane">
<img src="http://i.stack.imgur.com/oURrw.png">
</div>
JavaScript
$('#pane').bind('mousewheel DOMMouseScroll', function(e){
var stage = $(this);
scaleData = getZoom(stage);
if ( e.originalEvent.detail < 0 ){
setZoom( scaleData.curScale * '.9', stage );
}
else{
setZoom( scaleData.curScale * '1.1', stage );
}
});
function setZoom(scale, el){
scale = Math.round(scale*10)/10;
el.attr({
style:
'zoom: '+scale+';'+
'-webkit-transform: scale('+scale+');'+
'-moz-transform: scale('+scale+');'+
'-o-transform: scale('+scale+');'+
'-ms-transform: scale('+scale+');'+
'transform: scale('+scale+');'
});
}
function getZoom(el){
var curZoom = el.css('zoom');
var curScale = el.css('transform') ||
el.css('-webkit-transform') ||
el.css('-moz-transform') ||
el.css('-o-transform') ||
el.css('-ms-transform');
if ( curScale === 'none' ){
curScale = 1;
}else{
//Parse retarded matrix string into array of values
var scaleArray = $.parseJSON(curScale.replace(/^\w+\(/,"[").replace(/\)$/,"]"));
//We only need one of the two scaling components as we are always scaling evenly across both axes
curScale = scaleArray[0];
}
return { curZoom: curZoom, curScale: curScale };
}
CSS
div#pane{
height: 20em;
margin: auto;
background-color: #ffffff;
overflow: scroll;
position: relative;
z-index: 0;
}
Thank you
If I understand correctly the solution is to change the 2nd line of the JS you provided in a JSFiddle to:
var stage = $(this).find('img');
I just included .find('img') to the end of what you had there. Previously you were zooming the entire div, now it zooms just the img tag.

Setting div Background using Trianglify

I have some problems using the Trianglify plugin. I would like to use it to set the background of a div. How can I do this? I couldn't find a proper example.
Here's my sample code:
<script>
var pattern = Trianglify({
width: window.innerWidth,
height: window.innerHeight
});
document.body.appendChild(pattern.canvas())
</script>
Also, can I have divs with different backgrounds that come from Trianglify?
One DIV
Here is an example of setting a DIV background to a Trianglify pattern. It cheats a bit and sets the DIV child node to the pattern but it should work for you.
var something = document.getElementById('something');
var dimensions = something.getClientRects()[0];
var pattern = Trianglify({
width: dimensions.width,
height: dimensions.height
});
something.appendChild(pattern.canvas());
The DIV has an id of something and the CSS styles are set on the div for height and width.
Working example JSFiddle: http://jsfiddle.net/u55cn0fh/
Multiple DIVs
We can easily expand this for multiple DIVs like so:
function addTriangleTo(target) {
var dimensions = target.getClientRects()[0];
var pattern = Trianglify({
width: dimensions.width,
height: dimensions.height
});
target.appendChild(pattern.canvas());
}
JSFiddle: http://jsfiddle.net/u55cn0fh/1/
Multiple DIVs as a true background-image
The above are simply appending the pattern to the DIV as a child node instead of setting it as a background. The good news is that we can indeed use the background-image CSS property like so:
function addTriangleTo(target) {
var dimensions = target.getClientRects()[0];
var pattern = Trianglify({
width: dimensions.width,
height: dimensions.height
});
target.style['background-image'] = 'url(' + pattern.png() + ')';
}
JSFiddle: http://jsfiddle.net/abL2kc2q/
I'm pretty late to this answer, but I think it's still valuable:
If you aren't satisfied with using pattern.png() to generate a PNG version (like I wasn't), then you can also use pattern.svg() to set a SVG background with a little more work.
I always tend to lean towards using SVG backgrounds as typically they are crisper. In a test case I ran, using the SVG version also saved bits (although it's relative because sending a Trianglify background is a bit of a burden to begin with).
Characters in the base64 SVG encoding: 137284
Characters in the base64 PNG encoding: 195288
Converting the SVG to a base64 encoding then setting it as the background image can be achieved as follows:
// Create the Trianglify pattern
var pattern = Trianglify({
cell_size: 30,
variance: 0.75,
x_colors: 'random',
y_colors: 'match_x',
palette: Trianglify.colorbrewer,
stroke_width: 1.51,
});
// Serialize the SVG object to a String
var m = new XMLSerializer().serializeToString(pattern.svg());
// Perform the base64 encoding of the String
var k = window.btoa(m);
// Query the element to set the background image property
var element = document.getElementsByTagName('header')[0];
// Set the background image property, including the encoding type header
element.style.backgroundImage = 'url("data:image/svg+xml;base64,' + k + '")';
Hope this helps!
IN my case I had to use a class to use multiple instances of the same background image :
var obj = {
'trianglifiedlightblue': ['#a5cade', '#b7d5e5', '#d5e6f0', '#006ab4', '#e8f2f7', '#cee2ed'],
'trianglifiedbleu': ['#004e83', '#005f9f', '#004879', '#006ab4', '#004777', '#005f9f'],
'trianglifiedviolet': ['#680036', '#830447', '#e62f8e', '#c76c9b'],
'trianglifiedrouge': ['#5f0308', '#851117', '#cf363f', '#e86d74']
};
function addTriangle(classname) {
targets = document.getElementsByClassName(classname);
for (i = 0; i < targets.length; i++) {
target = targets[i];
if (target != null) {
var dimensions = target.getClientRects()[0];
var pattern = Trianglify({
width: dimensions.width,
height: dimensions.height,
x_colors: obj[classname],
cell_size: 100 + Math.random() * 200
});
target.style['background-image'] = 'url(' + pattern.png() + ')';
}
}
}
addTriangle('trianglifiedlightblue');
addTriangle('trianglifiedbleu');
addTriangle('trianglifiedviolet');
addTriangle('trianglifiedrouge');
div {
height: 100px;
width: 500px;
margin:10px;
float:left;
background:#efefef;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/trianglify/0.2.1/trianglify.min.js"></script>
<div class="trianglifiedlightblue"></div>
<div class="trianglifiedbleu"></div>
<div class="trianglifiedviolet"></div>
<div class="trianglifiedrouge"></div>
or if you prefer to keep an ID you can try this :
http://jsfiddle.net/thecpg/Lecdhce6/10/

Using JavaScript to change CSS style based on page width

I'm working on an assignment for a class where I have to make an external JavaScript file that checks a page's current width and changes the linked CSS style based on it, and have that occur whenever the page loads or is resized. Normally, we are given an example to base our assignment off of, but that was not the case this time around.
Essentially we are to use an if...then statement to change the style. I have no clue what the appropriate statements would be for the function. I've looked around and the potential solutions are either too advanced for the class or don't go over what I need. As far as I know I cannot use jQuery or CSS queries.
If someone could give me an example of how I would write this out, I would be very appreciative.
Try this code
html is
<div id="resize" style="background:red; height: 100px; width: 100px;"></div>
javascript is
var resize = document.getElementById('resize');
window.onresize=function(){
if(window.innerWidth <= 500) {
resize.style = "background:blue; height: 100px; width: 100px;";
}
else {
resize.style = "background:red; height: 100px; width: 100px;";
}
};
i have create a sample for you look at here TESTRESIZE
try resizing your browser and let me know if it is what you are looking for.
//Use This//
function adjustStyle() {
var width = 0;
// get the width.. more cross-browser issues
if (window.innerHeight) {
width = window.innerWidth;
} else if (document.documentElement && document.documentElement.clientHeight) {
width = document.documentElement.clientWidth;
} else if (document.body) {
width = document.body.clientWidth;
}
// now we should have it
if (width < 799) {
document.getElementById("CSS").setAttribute("href", "http://carrasquilla.faculty.asu.edu/GIT237/smallStyle.css");
} else {
document.getElementById("CSS").setAttribute("href", "http://carrasquilla.faculty.asu.edu/GIT237/largeStyle.css");
}
}
// now call it when the window is resized.
window.onresize = function () {
adjustStyle();
};
window.onload = function () {
adjustStyle();
};
Use CSS and media queries. It's bad idea to change style by js.

Categories