Easel.js SpriteSheetBuilder() causing browser to lock up - easeljs

I have used the SpriteSheetBuilder in Easel.js to create sprite sheets on the fly. The issue I am having is that I am only able to draw about 5 or 6 instances of the sprite to the canvas before the browser locks up. It is causing significant cpu and memory issues. I am having trouble pinpointing why this is happening. It must be my implementation. My sprite sheet is comprised of about 100 frames but he images I am using are only 8kbs each.
What I am making is a conga line animation. I will be making a call to the server to get the faces that will be composited on the dancer sprites with the sprite builder. Any insight into this issue is great appreciated! Here is my code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>EaselJS Example: SpriteSheetBuilder</title>
<script src="easeljs-0.8.2.min.js"></script>
<style>
#dancers img {
display: none;
}
canvas {
background: red;
width: 100%;
}
button {
width: 50px;
height: 50px;
}
</style>
</head>
<body>
<div id="dancers">
<img id="stage" src="stage.jpg">
<img id="curtains" src="curtains.png">
<img id="conga_1" src="conga_1.png">
<img id="conga_2" src="conga_2.png">
<img id="conga_3" src="conga_3.png">
<img id="conga_4" src="conga_4.png">
<img id="conga_5" src="conga_5.png">
<img id="conga_6" src="conga_6.png">
<img id="conga_7" src="conga_7.png">
<img id="conga_8" src="conga_8.png">
<img id="conga_9" src="conga_9.png">
<img id="conga_10" src="conga_10.png">
<img id="conga_11" src="conga_11.png">
<img id="conga_12" src="conga_12.png">
<img id="conga_13" src="conga_13.png">
<img id="conga_14" src="conga_14.png">
<img id="conga_15" src="conga_15.png">
<img id="conga_16" src="conga_16.png">
<img id="conga_17" src="conga_17.png">
<img id="conga_18" src="conga_18.png">
<img id="conga_19" src="conga_19.png">
<img id="conga_20" src="conga_20.png">
<img id="conga_21" src="conga_21.png">
<img id="conga_22" src="conga_22.png">
<img id="conga_23" src="conga_23.png">
<img id="conga_24" src="conga_24.png">
<img id="conga_25" src="conga_25.png">
<img id="conga_26" src="conga_26.png">
<img id="conga_27" src="conga_27.png">
<img id="conga_28" src="conga_28.png">
<img id="conga_29" src="conga_29.png">
<img id="conga_30" src="conga_30.png">
<img id="conga_31" src="conga_31.png">
<img id="conga_32" src="conga_32.png">
<img id="conga_33" src="conga_33.png">
<img id="conga_34" src="conga_34.png">
<img id="conga_35" src="conga_35.png">
<img id="conga_36" src="conga_36.png">
<img id="conga_37" src="conga_37.png">
<img id="conga_38" src="conga_38.png">
<img id="conga_39" src="conga_39.png">
<img id="conga_40" src="conga_40.png">
<img id="conga_41" src="conga_41.png">
<img id="conga_42" src="conga_42.png">
<img id="conga_43" src="conga_43.png">
<img id="conga_44" src="conga_44.png">
<img id="conga_45" src="conga_45.png">
<img id="conga_46" src="conga_46.png">
<img id="conga_47" src="conga_47.png">
<img id="conga_48" src="conga_48.png">
<img id="conga_49" src="conga_49.png">
<img id="conga_50" src="conga_50.png">
<img id="conga_51" src="conga_51.png">
<img id="conga_52" src="conga_52.png">
<img id="conga_53" src="conga_53.png">
<img id="conga_54" src="conga_54.png">
<img id="conga_55" src="conga_55.png">
<img id="conga_56" src="conga_56.png">
<img id="conga_57" src="conga_57.png">
<img id="conga_58" src="conga_58.png">
<img id="conga_59" src="conga_59.png">
<img id="conga_60" src="conga_60.png">
<img id="conga_61" src="conga_61.png">
<img id="conga_62" src="conga_62.png">
<img id="conga_63" src="conga_63.png">
<img id="conga_64" src="conga_64.png">
<img id="conga_65" src="conga_65.png">
<img id="conga_66" src="conga_66.png">
<img id="conga_67" src="conga_67.png">
<img id="conga_68" src="conga_68.png">
<img id="conga_69" src="conga_69.png">
<img id="conga_70" src="conga_70.png">
<img id="conga_71" src="conga_71.png">
<img id="conga_72" src="conga_72.png">
<img id="conga_73" src="conga_73.png">
<img id="conga_74" src="conga_74.png">
<img id="conga_75" src="conga_75.png">
<img id="conga_76" src="conga_76.png">
<img id="conga_77" src="conga_77.png">
<img id="conga_78" src="conga_78.png">
<img id="conga_79" src="conga_79.png">
<img id="conga_80" src="conga_80.png">
<img id="conga_81" src="conga_81.png">
<img id="conga_82" src="conga_82.png">
<img id="conga_83" src="conga_83.png">
<img id="conga_84" src="conga_84.png">
<img id="conga_85" src="conga_85.png">
<img id="conga_86" src="conga_86.png">
<img id="conga_87" src="conga_87.png">
<img id="conga_88" src="conga_88.png">
<img id="conga_89" src="conga_89.png">
<img id="conga_90" src="conga_90.png">
<img id="conga_91" src="conga_91.png">
<img id="conga_92" src="conga_92.png">
<img id="conga_93" src="conga_93.png">
<img id="conga_94" src="conga_94.png">
<img id="conga_95" src="conga_95.png">
<img id="conga_96" src="conga_96.png">
<img id="conga_97" src="conga_97.png">
<img id="conga_98" src="conga_98.png">
<img id="conga_99" src="conga_99.png">
<img id="conga_100" src="conga_100.png">
<img id="conga_101" src="conga_101.png">
<img id="conga_102" src="conga_102.png">
<img id="conga_103" src="conga_103.png">
<img id="conga_104" src="conga_104.png">
<img id="conga_105" src="conga_105.png">
<img id="conga_106" src="conga_106.png">
<img id="conga_107" src="conga_107.png">
<img id="conga_108" src="conga_108.png">
</div>
<div>
<canvas id="testCanvas" width="1920" height="1080"></canvas>
</div>
<button id="add_dancer" onclick="addDancer();">add dancer</button>
<script src="jjcoords.js"></script>
<script id="editable">
var canvas = document.getElementById("testCanvas");
var stage = new createjs.Stage(canvas);
var containers = [];
var congaDancers = [];
var counter = 0;
function init() {
var stageImg = new createjs.Bitmap(document.getElementById('stage').src);
stage.addChild(stageImg);
buildSprite();
var curtains = new createjs.Bitmap(document.getElementById('curtains').src);
stage.addChild(curtains);
createjs.Ticker.addEventListener("tick", tick);
}
function buildSprite() {
var container = createFrame('ryan3.png', document.getElementById(coords[counter].id).src, coords[counter].x, coords[counter].y, coords[counter].scale, coords[counter].rotation);
containers.push(container);
}
function createFrame(consumerImg, dancerFrame, x, y, scale, rot) {
var bitmap;
var container = new createjs.Container();
var consumerFace = new Image();
consumerFace.src = consumerImg;
consumerFace.onload = function () {
var dancer = new createjs.Bitmap(dancerFrame);
dancer.scaleX = 1.5;
dancer.scaleY = 1.5;
dancer.regX = dancer.image.width / 2 | 0;
dancer.regY = dancer.image.height / 2 | 0;
dancer.x = 100;
dancer.y = 100;
bitmap = new createjs.Bitmap(consumerFace.src);
bitmap.regX = bitmap.image.width / 2 | 0;
bitmap.regY = bitmap.image.height / 2 | 0;
bitmap.scaleX = scale;
bitmap.scaleY = scale;
bitmap.x = x;
bitmap.y = y;
bitmap.rotation = rot;
// bitmap.alpha = .5;
container.name = 'ryan' + counter;
container.addChild(dancer, bitmap);
container.x = 700;
container.y = 250;
counter++;
if (counter < coords.length) {
buildSprite();
} else {
spriteBuilder();
}
};
return container;
}
function spriteBuilder() {
var square = new createjs.Container();
// stage.addChild(containers[107]);
// containers[107].y = 500;
// create the sprite sheet builder:
var builder = new createjs.SpriteSheetBuilder();
var frames = [];
for (var j = 0; j < containers.length; j++) {
index = builder.addFrame(containers[j], null, 1, function (target, data) {
}, j);
// save off the index of each frame in order to use when defining the animation:
frames.push(index);
}
// create an animation named square that comprises all of the frames we just added:
// we're also telling it to loop the animation and setting a frequency so it updates every 8 ticks:
builder.addAnimation("square", frames, true, 1);
// run the build operation, and grab the resulting sprite sheet:
// we could also do this asynchronously with buildAsync(...)
var spriteSheet = builder.build();
var square2 = new createjs.Sprite(spriteSheet, "square");
square2.scaleX = .6;
square2.scaleY = .6;
square2.x = 250;
square2.y = 620;
stage.addChild(square2);
// add in the generated spritesheet image for demo purposes:
// stage.addChild(new createjs.Bitmap(spriteSheet._images[0])).set({x: 75, y: 150});
// we want to do some work before we update the canvas,
// otherwise we could use Ticker.addEventListener("tick", stage);
congaDancers.push(square2);
console.log(congaDancers);
}
var xpos = 200;
function addDancer() {
counter = 0;
containers = [];
buildSprite();
xpos = xpos + 200;
congaDancers[congaDancers.length - 1].x = xpos;
}
function tick() {
stage.update(event);
}
init();
</script>
</body>
</html>

This is actually pretty simple. By caching 100+ images for each dancer, you are generating a TON of images/canvases in memory. This is 75,000,000 pixels for each dancer.
SpriteSheets are a good way to cache content that is complex. However, drawing and swapping this many large textures may have a detrimental effect, instead of offering any benefits.
There are lots of better ways to do this.
If you really want to use SpriteSheets, then make one, and add the custom face over top. So create a container that has the dancer body, and a face sprite/bitmap. You can make as many of these as you like, and not generate a new set of giant images for each one.
Rather than make a new SpriteSheet using SpriteSheetBuilder, just generate a definition that uses all your images you are already creating. The SpriteSheet supports multiple images, so you should be able to point each frame to a new image. Then take the same approach as earlier
Where did this art come from? If you are animating using body part images, consider doing that in EaselJS instead of saving bitmaps. A bunch of small body parts reused will be way more performant.
If you started with Vectors, consider using Adobe Animate to export them. This will generate you scalable vector content that you can use instead. Granted, a whole ton of vectors on canvas is not ideal, but it will not be taxing your memory the way you are now.
I would also consider preloading the images to create a better experience.
Hope this helps!

Related

How to add webp images as background image of a div with fallback to Jpeg

I want to add a background image to a div in webp format with fallback to jpg.
If it is img tag, we can call it as:
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg">
</picture>
And what can we do in case of <div class="carousel-item active" style="background-image: url('images/image.jpeg')">
Thank you for any help!
Here is a my solution...
You load the image with a hidden picture element and fetch with js the loaded image (with fallback to jpg or png). Finally you set the loaded image with jquery as the background of the parent div.
Maybe there is a better solution in the field.
function makeBgImage( img ){
let srcImage;
if ( typeof img.currentSrc === "undefined" ){
//Old browser
srcImage = img.src;
}else{
//Modern browser
srcImage = img.currentSrc;
}
let ref = $(img).parents('div:first');
ref.css('background', 'url(' + srcImage + ')');
if( ref.hasClass('contain') ){
ref.css('background-size', 'contain');
}else{
ref.css('background-size', 'cover');
}
ref.css('background-position', 'center');
ref.css('background-repeat', 'no-repeat');
}
.hidden{display: none;}
.img-container{
width: 100%;
padding-top: 62.5%; /* 8:5 Aspect Ratio */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="img-container">
<picture class="hidden">
<source srcset="image.webp" type="image/webp">
<img onload="makeBgImage(this)" alt="Image" src="image.png">
</picture>
</div>

Combining Swiper slider and photoswipe

I'm looking for a combination of Swiper slider and Photoswipe (Or other lightbox).
Trying to make a product slider with 3 products in a slide.
Each product has a lightbox/modal with video and a gallery.
The modals are generated within the boundaries of the product div.
When you click an 'open gallery' / 'show video' link. The lightbox opens fullscreen.
The problem I'm having is: the lightbox won't (but has to) exceed the boundary of the slider product boundary.
Looking for a solution.
Something like an empty modal/lightbox containers outside the slider with dynamic content when an 'open modal' link is clicked within the product slide.
You can check it, here is example:
<header>
<h1>
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">Swiper.js (5.3.7)</a> &
<a title="photoswipe" href="http://photoswipe.com/" target="_blank">Photoswipe.js (4.1.3)</a> - Mobile Native feel
slider gallery
</h1>
<p>Combine two of the most powerfull JS plugins (Endless options / Great docs / Fast / Modern / Mobile freindly) -
<a title="swiper.js" href="http://idangero.us/swiper/" target="_blank">SWIPER</a> IS PERFECT FOR THIS IDEA BEACUSE OF
ITS unique <code>preventClicks</code> Parameter (Prevent accidental unwanted clicks on links during swiping) -
<strong>Works like magic</strong>. Also its really <b>hard</b> to find - Code example of working photoswipe
combination with any slider out there(slick, flickity, owl etc.) and
in general slider & lightbox - so i hope this example be usefull for you.</p>
</header>
<!-- https://swiperjs.com/get-started/ -->
<!-- Slider main container -->
<div class="swiper-container">
<!-- Additional required wrapper -->
<ul class="swiper-wrapper my-gallery" itemscope itemtype="http://schema.org/ImageGallery">
<!-- Slides -->
<li id="1" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a id="first" title="click to zoom-in" href="https://picsum.photos/id/1079/1200/600" itemprop="contentUrl" data-size="1200x600">
<img src="https://picsum.photos/id/1079/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="2" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x601/AB47BC/ffffff?text=Zoom-image-2"
itemprop="contentUrl" data-size="1200x601">
<img src="http://placehold.it/600x300/AB47BC/ffffff?text=Thumbnail-image-2" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="3" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/EF5350/ffffff?text=Zoom-image-3" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/EF5350/ffffff?text=Thumbnail-image-3" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="4" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="http://placehold.it/1200x600/1976D2/ffffff?text=Zoom-image-4" itemprop="contentUrl" data-size="1200x600">
<img src="http://placehold.it/600x300/1976D2/ffffff?text=Thumbnail-image-4" itemprop="thumbnail" alt="Image description" />
</a>
</li>
<li id="5" class="swiper-slide" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject">
<a title="click to zoom-in" href="https://picsum.photos/id/1011/1200/600" itemprop="contentUrl"
data-size="1200x600">
<img src="https://picsum.photos/id/1011/1200/600" itemprop="thumbnail" alt="Image description" />
</a>
</li>
</ul>
<!-- Add Pagination -->
<div class="swiper-pagination"></div>
<!-- If we need navigation buttons -->
<div title="Prev" class="swiper-button-prev"></div>
<div title="Next" class="swiper-button-next"></div>
</div>
<!-- https://photoswipe.com/documentation/getting-started.html -->
<!-- add PhotoSwipe (.pswp) element to DOM -
Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<!-- Background of PhotoSwipe.
It's a separate element, as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
<!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
<!-- don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
</div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
<!-- Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
<button class="pswp__button pswp__button--share" title="Share"></button>
<button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
<button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
<!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
<!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
</div>
</div>
</div>
</div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
</div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
</button>
<button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
</button>
<div class="pswp__caption">
<div class="pswp__caption__center"></div>
</div>
</div>
</div>
</div>
<!-- ////////////////////////
DOCS
////////////////////////////
-->
<!-- Include Tippy -->
<script src="https://unpkg.com/tippy.js#3/dist/tippy.all.min.js"></script>
<!-- OPTIONAL: Set the defaults for the auto-initialized tooltips -->
<script>
tippy('.swiper-button-prev', {
content: "Prev",
theme: "light",
arrow: true,
})
tippy('.swiper-button-next', {
content: "Next",
theme: "light",
arrow: true,
})
</script>
<section id="docs">
<br>
<br>
<br>
<hr>
<h2>Noted / Important</h2>
<ol>
<li>
<h3>
A non-jQuery dependent
</h3>
</li>
<li>
<h3>Cdns</h3>
<h4>Head (CSS)</h4>
<code>
<!-- photoswipe CSS -->
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.css" />
<br>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/default-skin/default-skin.min.css" />
<br>
<!-- swiper CSS -->
<br>
<link rel="stylesheet" href="https://unpkg.com/swiper/css/swiper.min.css" />
</code>
<h4>Before body (JS)</h4>
<code>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe.min.js"></script>
<br>
<script src="https://cdnjs.cloudflare.com/ajax/libs/photoswipe/4.1.3/photoswipe-ui-default.min.js"></script>
<br>
<script src="https://unpkg.com/swiper/js/swiper.min.js"></script>
<br>
<!-- copy-paste the code under codepen js tab her (wrap with script tag) -->
</code>
</li>
<li>
<h3>Loop & Counter</h3>
<p>
<strong> Wont work well </strong> with swiper: <code>loop = true;</code> & photoswipe: <code>counterEl:
true,</code>(What is counter? example: 1/5...2/5) - "loop" duplicate images - the photoswipe counter will be
wrong. *** If you dont want a
loop - you can set photoswipe counter <code>counterEl: true,</code>
</p>
</li>
<li>
<h3><a target="_blank" href="https://schema.org/ImageGallery">Schema.org - ImageGallery</a> Markup (0 ERRORS 0 WARNINGS )</h3>
<p>
Schema.org markup + semantic HTML: use unordered (bulleted) list (If you want a <code>div</code> under photoswipe - change JS -
<strong>"(find) control+f-->"</strong> tagname value) . Copy-paste - this code to check: <a target="_blank"
href="https://search.google.com/structured-data/testing-tool">Structured Data Testing Tool - Google</a>
</p>
</li>
<li>
<h3>Match index - BY API</h3>
<p>
<strong>
Extra CODE "match index"
</strong> - EXAMPLE: When you click(zoom) image1 -- goes to image 2 - close image2 (X) - also the swiper update
is position (<strong>BETTER</strong> User Experience) (find<kbd>(ctr +f)</kbd>-->
<code>mySwiper.slideTo(getCurrentIndex, false);</code>) -
This idea miss
in most slider & lightbox examples/plugins mixed.
<br>
Very simple code idea (100% API solution) - get photoswipe index (for example 2) and swiper slideTo index (2 - in this example).
<ul>
<li >
<a target="_blank" href="https://photoswipe.com/documentation/api.html">Photoswipe API - <strong>pswp.getCurrentIndex()</strong></a>
<li style="border-top-width: 0;"> <a target="_blank" href="https://swiperjs.com/api/#methods">Swiper API - <strong>slideTo(index);</strong></a>
</li>
</li>
</ul>
</p>
</li>
<li>
<h3>Photoswipe options</h3>
<p>
JS - line (find) -ctr +f --> the term:<code>// define options (if needed)</code>. You find endless options for
<strong>photoswipe</strong> - This is the place to add/modify options. Full Options list her
<a href="http://photoswipe.com/documentation/options.html" target="_blank">PhotoSwipe
Options</a>
</p>
</li>
<li>
<h3>SWIPER options</h3>
<h4>slideperview</h4>
<p>
<code>slideperview</code> - option1: Set number (1,2,3 and so on) - example |||||
option2(<b>"Carousel Mode"</b> this example): Set to "<code>auto</code>"
than add CSS <a href="https://www.w3schools.com/cssref/pr_dim_width.asp" target="_blank">width
Property</a></code> <code>.swiper-slide</code> (in thie case eash slide is 88% width) - example.
</p>
<h4>spaceBetween & centeredSlides</h4>
<p>
Space Between slide by js option <code>spaceBetween</code> - and also usefull to change
<code>centeredSlides</code>(true/flase). <br>
Swiper API
</p>
</li>
</ol>
<hr>
<h3>Related Example</h3>
<p>
<a title="FancyBox3 & Flickity" href="https://codepen.io/ezra_siton/pen/OQmjoq" target="_blank">#FancyBox3 -
lightbox & Flickity Slider</a>
</p>
</section>
/* zero custom styles for photoswipe */
/*==================================
SWIPER - minimal styling
===================================*/
/* semantic HTML - remove bullet and space from the list */
ul.swiper-wrapper {
list-style-type: none;
margin: 0;
padding: 0;
}
/* Swiper container */
.swiper-container {
max-width: 100%;
}
/* swiper responive image */
.swiper-container img {
width: 100%;
height: auto;
}
.swiper-slide {
/* Remove this if you want 1 slide perview - than change slidesPerView js-option to 1 -or- 2+ instead of 'auto' */
width: 80%;
}
/* Swiper custom pagination */
.swiper-pagination-bullet {
width: 34px;
height: 34px;
text-align: center;
line-height: 34px;
font-size: 14px;
color: #000;
opacity: 1;
background: rgba(0, 0, 0, 0.3);
transition: background-color 0.5s ease, color 0.5s ease;
}
/* Swiper custom pagination */
.swiper-pagination-bullet:hover {
transition: background-color 0.5s ease;
background: rgba(0, 0, 0, 1);
color: white;
}
/* Swiper custom pagination active state */
.swiper-pagination-bullet-active {
color: #fff;
background: black;
}
/*==================================================================
CODEPEN STYLES -(under codepen gear/setting icon)
==============================================++++++================*/
.tippy-tooltip.light-theme {
color: #26323d;
box-shadow: 0 0 20px 4px rgba(154, 161, 177, 0.15),
0 4px 80px -8px rgba(36, 40, 47, 0.25),
0 4px 4px -2px rgba(91, 94, 105, 0.15);
background-color: white;
}
.tippy-backdrop {
background-color: white;
}
.tippy-roundarrow {
fill: white;
}
/* Default (sharp) arrow */
.tippy-popper[x-placement^='top'] .tippy-tooltip.light-theme .tippy-arrow {
border-top-color: #fff;
}
.tippy-popper[x-placement^='bottom'] .tippy-tooltip.light-theme .tippy-arrow {
border-bottom-color: #fff;
}
.tippy-popper[x-placement^='left'] .tippy-tooltip.light-theme .tippy-arrow {
border-left-color: #fff;
}
.tippy-popper[x-placement^='right'] .tippy-tooltip.light-theme .tippy-arrow {
border-right-color: #fff;
}
/* Round arrow */
.tippy-tooltip.light-theme .tippy-roundarrow {
fill: #fff;
}
/* TOC
part one - Swiper instilaze
part two - photoswipe instilaze
part three - photoswipe define options
part four - extra code (update swiper index when image close and micro changes)
/* 1 of 4 : SWIPER ################################### */
var mySwiper = new Swiper(".swiper-container", {
// If swiper loop is true set photoswipe counterEl: false (line 175 her)
loop: true,
/* slidesPerView || auto - if you want to set width by css like flickity.js layout - in this case width:80% by CSS */
slidesPerView: "auto",
spaceBetween: 10,
centeredSlides: true,
slideToClickedSlide: false,
autoplay: { /* remove/comment to stop autoplay */
delay: 3000,
disableOnInteraction: false /* true by deafult */
},
// If we need pagination
pagination: {
el: ".swiper-pagination",
clickable: true,
renderBullet: function(index, className) {
return '<span class="' + className + '">' + (index + 1) + "</span>";
}
},
// Navigation arrows
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
// keyboard control
keyboard: {
enabled: true,
}
});
// 2 of 4 : PHOTOSWIPE #######################################
// https://photoswipe.com/documentation/getting-started.html //
var initPhotoSwipeFromDOM = function(gallerySelector) {
// parse slide data (url, title, size ...) from DOM elements
// (children of gallerySelector)
var parseThumbnailElements = function(el) {
var thumbElements = el.childNodes,
numNodes = thumbElements.length,
items = [],
figureEl,
linkEl,
size,
item;
for (var i = 0; i < numNodes; i++) {
figureEl = thumbElements[i]; // <figure> element
// include only element nodes
if (figureEl.nodeType !== 1) {
continue;
}
linkEl = figureEl.children[0]; // <a> element
size = linkEl.getAttribute("data-size").split("x");
// create slide object
item = {
src: linkEl.getAttribute("href"),
w: parseInt(size[0], 10),
h: parseInt(size[1], 10)
};
if (figureEl.children.length > 1) {
// <figcaption> content
item.title = figureEl.children[1].innerHTML;
}
if (linkEl.children.length > 0) {
// <img> thumbnail element, retrieving thumbnail url
item.msrc = linkEl.children[0].getAttribute("src");
}
item.el = figureEl; // save link to element for getThumbBoundsFn
items.push(item);
}
return items;
};
// find nearest parent element
var closest = function closest(el, fn) {
return el && (fn(el) ? el : closest(el.parentNode, fn));
};
// triggers when user clicks on thumbnail
var onThumbnailsClick = function(e) {
e = e || window.event;
e.preventDefault ? e.preventDefault() : (e.returnValue = false);
var eTarget = e.target || e.srcElement;
// find root element of slide
var clickedListItem = closest(eTarget, function(el) {
return el.tagName && el.tagName.toUpperCase() === "LI";
});
if (!clickedListItem) {
return;
}
// find index of clicked item by looping through all child nodes
// alternatively, you may define index via data- attribute
var clickedGallery = clickedListItem.parentNode,
childNodes = clickedListItem.parentNode.childNodes,
numChildNodes = childNodes.length,
nodeIndex = 0,
index;
for (var i = 0; i < numChildNodes; i++) {
if (childNodes[i].nodeType !== 1) {
continue;
}
if (childNodes[i] === clickedListItem) {
index = nodeIndex;
break;
}
nodeIndex++;
}
if (index >= 0) {
// open PhotoSwipe if valid index found
openPhotoSwipe(index, clickedGallery);
}
return false;
};
// parse picture index and gallery index from URL (#&pid=1&gid=2)
var photoswipeParseHash = function() {
var hash = window.location.hash.substring(1),
params = {};
if (hash.length < 5) {
return params;
}
var vars = hash.split("&");
for (var i = 0; i < vars.length; i++) {
if (!vars[i]) {
continue;
}
var pair = vars[i].split("=");
if (pair.length < 2) {
continue;
}
params[pair[0]] = pair[1];
}
if (params.gid) {
params.gid = parseInt(params.gid, 10);
}
return params;
};
var openPhotoSwipe = function(
index,
galleryElement,
disableAnimation,
fromURL
) {
var pswpElement = document.querySelectorAll(".pswp")[0],
gallery,
options,
items;
items = parseThumbnailElements(galleryElement);
// #################### 3/4 define photoswipe options (if needed) ####################
// https://photoswipe.com/documentation/options.html //
options = {
/* "showHideOpacity" uncomment this If dimensions of your small thumbnail don't match dimensions of large image */
//showHideOpacity:true,
// Buttons/elements
closeEl: true,
captionEl: true,
fullscreenEl: true,
zoomEl: true,
shareEl: false,
counterEl: false,
arrowEl: true,
preloaderEl: true,
// define gallery index (for URL)
galleryUID: galleryElement.getAttribute("data-pswp-uid"),
getThumbBoundsFn: function(index) {
// See Options -> getThumbBoundsFn section of documentation for more info
var thumbnail = items[index].el.getElementsByTagName("img")[0], // find thumbnail
pageYScroll =
window.pageYOffset || document.documentElement.scrollTop,
rect = thumbnail.getBoundingClientRect();
return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
}
};
// PhotoSwipe opened from URL
if (fromURL) {
if (options.galleryPIDs) {
// parse real index when custom PIDs are used
// http://photoswipe.com/documentation/faq.html#custom-pid-in-url
for (var j = 0; j < items.length; j++) {
if (items[j].pid == index) {
options.index = j;
break;
}
}
} else {
// in URL indexes start from 1
options.index = parseInt(index, 10) - 1;
}
} else {
options.index = parseInt(index, 10);
}
// exit if index not found
if (isNaN(options.index)) {
return;
}
if (disableAnimation) {
options.showAnimationDuration = 0;
}
// Pass data to PhotoSwipe and initialize it
gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
gallery.init();
/* ########### PART 4 - EXTRA CODE ########### */
/* EXTRA CODE (NOT FROM photoswipe CORE) -
1/2. UPDATE SWIPER POSITION TO THE CURRENT ZOOM_IN IMAGE (BETTER UI) */
// photoswipe event: Gallery unbinds events
// (triggers before closing animation)
gallery.listen("unbindEvents", function() {
// This is index of current photoswipe slide
var getCurrentIndex = gallery.getCurrentIndex();
// Update position of the slider
mySwiper.slideTo(getCurrentIndex, false);
// 2/2. Start swiper autoplay (on close - if swiper autoplay is true)
mySwiper.autoplay.start();
});
// 2/2. Extra Code (Not from photo) - swiper autoplay stop when image zoom */
gallery.listen('initialZoomIn', function() {
if(mySwiper.autoplay.running){
mySwiper.autoplay.stop();
}
});
};
// loop through all gallery elements and bind events
var galleryElements = document.querySelectorAll(gallerySelector);
for (var i = 0, l = galleryElements.length; i < l; i++) {
galleryElements[i].setAttribute("data-pswp-uid", i + 1);
galleryElements[i].onclick = onThumbnailsClick;
}
// Parse URL and open gallery if it contains #&pid=3&gid=1
var hashData = photoswipeParseHash();
if (hashData.pid && hashData.gid) {
openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
}
};
// execute above function
initPhotoSwipeFromDOM(".my-gallery");
https://codepen.io/ezra_siton/pen/XNpJaX/
instead using photoswipe, use only swiper like in this demo I did make:
<-------html------>
<div class="swiper-container horizontal">
<div class="swiper-wrapper">
<div class="swiper-slide"><div class="swiper-container vertical">
<div class="swiper-wrapper vertical">
<div class="swiper-slide vertical">
Slide 1
</div>
<div class="swiper-slide vertical">
Slide 1.1
</div>
<div class="swiper-slide vertical">
Slide 1.2
</div>
<div class="swiper-slide vertical">
Slide 1.3
</div>
</div>
<div class="swiper-pagination vertical"></div>
</div></div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
<div class="swiper-slide">Slide 4</div>
<div class="swiper-slide">Slide 5</div>
<div class="swiper-slide">Slide 6</div>
<div class="swiper-slide">Slide 7</div>
<div class="swiper-slide">Slide 8</div>
<div class="swiper-slide">Slide 9</div>
<div class="swiper-slide">Slide 10</div>
</div>
<!-- Add Pagination -->
<div class="swiper-pagination horizontal"></div>
</div>
<!-- Swiper JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/3.3.1/js/swiper.min.js"></script>
<script>
var swiper = new Swiper('.swiper-container.horizontal', {
pagination: '.swiper-pagination.horizontal',
direction: 'horizontal',
slidesPerView: 1,
paginationClickable: true,
spaceBetween: 30,
mousewheelControl: true
});
</script>
<script>
var swiper = new Swiper('.swiper-container.vertical', {
pagination: '.swiper-pagination',
direction: 'vertical',
slidesPerView: 1,
paginationClickable: true,
spaceBetween: 30,
mousewheelControl: true
});
</script>
<---------html end----------->
<--------css----------->
html, body {
position: relative;
height: 100%;
}
body {
background: #eee;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
font-size: 14px;
color:#000;
margin: 0;
padding: 0;
}
.swiper-container {
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
}
.swiper-slide {
text-align: center;
font-size: 18px;
background: #fff;
/* Center slide text vertically */
display: -webkit-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
}
<---------css end------------->
https://jsfiddle.net/120ngmoh/

tt_news: wrap around image including caption?

I am trying to get a wrapper element around an image including its caption in the single view in tt_news for Typo3 6.1. How would I do that?
So far I only figured out how to do that for either all images
plugin.tt_news.displaySingle.imageWrapIfAny = ...
or the caption itself
plugin.tt_news.displaySingle.caption_stdWrap.dataWrap = ...
. But I have no clue how to create a wrapper for each single image including its caption...
Thanks in advance!
I know this is quite old, but as a follow-up that works in TYPO3 6.x you can use:
displaySingle.image.wrap = <div class="news-img">|
displaySingle.caption_stdWrap >
displaySingle.caption_stdWrap.wrap = <p class="news-single-imgcaption">|</p></div>
So each image and caption gets wrapped by <div class="news-img">...</div>
The following HTML will be provided by the below TS:
<div class="newsImage">
<div class="imageHolder">
<img />
</div>
<div class="captionHolder">
Caption
</div>
</div>
TypoScript:
displaySingle {
imageWrapIfAny = <div class="newsImage">|</div>
image {
file.maxW >
file.maxH >
file.width = 220
stdWrap.wrap = <div class="imageHolder">|</div>
altText.field = imagealttext
}
caption_stdWrap >
caption_stdWrap.wrap = <div class="captionHolder">|</div>
}

jQuery Click on next element

I have a problem with programming jQuery and I hope that you can help me even if a speak english like an alien %).
I'm doing a site with horizontal scrolling, you can see it here http://www.kinetics.ru/index_kin.html
I have a menu at the left and two buttons PREV and NEXT at the bottom.
Menu is working correctly, but I need to let the bottom buttons work.
By clicking for example on NEXT I need that code made a click on next menu btn after the one that have style 'on'.
The code is:
<div class="menu">
<ul class="nav">
<li id="main"><img src="img/menu/about.png" /></li>
<li><img src="img/menu/uslugi.png"/></li>
<li><img src="img/menu/portfolio.png"/></li>
<li><img src="img/menu/clients.png"/></li>
<li><img src="img/menu/contacts.png"/></li>
</ul>
</div>
...
<div class="bottomNav" style="position: absolute; z-index: 11">
<div style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed" class="back"><img src="img/back.png"</div>
<div style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" id="next"><img src="img/forward.png"</div>
And my not working jQuery code for next btn:
$('bottomNav, #next img').click(function(){
if ($('ul.nav').find('img').hasClass('on')){
$('ul.nav').next().click();
}
});
Also I've tried to do like this:
$('bottomNav, #next img').click(function(){
$('ul.nav img').click(function(){
if ($(this).hasClass('on')){
$(this).next().click();}
});
});
P.S. Sorry for my noobish question. I just got a task at work to make a site and nobody cares that a designer is NOT the same as web-designer. I have no possibility to learn first, I have to learn and do at same time.
You have to review jQuery selectors and as well your HTML code, it's terribly wrong. Firebug says:
I modified your code in proper way:
<div class="bottomNav" style="position: absolute; z-index: 11">
<div id="prev" style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed">
<img src="img/back.png" />
</div>
<div id="next" style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" >
<img src="img/forward.png" />
</div>
</div>
JavaScript (It's pretty simple and I didn't test it, but it should give you an idea).
$('#prev').click(function () {
var $activeLi = getActiveLi();
var $prevLi = $parentLi.prev();
if ($prevLi.length > 0) {
$prevLi.find('a').click();
}
});
$('#next').click(function () {
var $activeLi = getActiveLi();
var $nextLi = $parentLi.next();
if ($nextLi.length > 0) {
$nextLi.find('a').click();
}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.closest('li');
return $activeLi;
};
But beeing seriously: before continuing developing your web site you seriously should put some effort in learnin hot to use jQuery and how to structure HTML...
Solved!
Thank you again!
Your code was really helpfull.
I have modified it a little bit for my needs and also changed in line
var $activeLi = $activeImg.closest('li');
closest('li') to parents('li'), cause it wasn't working like that.
So now code looks like this:
$('#prev').click(function () {
var $prevLi = getActiveLi().prev();
if ($prevLi.length > 0){$prevLi.find('img').click();}
});
$('#next').click(function () {
var $nextLi = getActiveLi().next();
if ($nextLi.length > 0){$nextLi.find('img').click();}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.parents('li');
return $activeLi;
};
Also i guess, that i have understood why firebug was showing so big disaster in code... i have forgot to close the tag :)))
Anyway...MANY THANKS TO YOU!
P.S. third day reading a giant book about jQuery :)

updating dom text based on click in jquery

My Simple HTML Structure:
<html>
<body>
<p id="5">
My Text Qoted Here -
<span class="author">Author </span>
<br>
<span class="uptext">20</span>
<img class="im" class="upimg"
src="http://bgathinktank.files.wordpress.com/2011/01/vote-button.jpg" />
<img class="im" class="downimg"
src="http://2.bp.blogspot.com/_XeuZ1yDnv4Q/TSUkAT6T1dI/AAAAAAAADR8/nPHP4JvVxy8/s1600/vote.jpg" />
<span class="downtext">5</span>
</p>
</body>
</html>
as can be seen there are two images, and i want to change the count of the image based on which image is clicked.
i write the following jquery code for it:
$(function() {
$(".upimg").click(function(e) {
e.preventDefault();
alert("here1");
curr_val = $(this).closest(".uptext").text();
nos = parseInt(curr_val, 10) + 1;
$(this).closest(".uptext").text(nos);
});
$(".downimg").click(function(e) {
e.preventDefault();
alert("here2");
curr_val = $(this).closest(".downtext").text();
nos = parseInt(curr_val, 10) + 1;
$(this).closest(".downtext").text(nos);
});
});
But it does not seem to respond.
You can find the fiddle here
Your <img> tags have two class attributes:
<img class="im" class="upimg"
Try changing that to
<img class="im upimg"
Also, the closest() method returns the closest parent element, not an adjacent sibling, so to find the correct uptext element, you need to use:
$(this).prev().text()
you cant use 2 class attribute
you have to do like that
<img class="im upimg"
src="http://bgathinktank.files.wordpress.com/2011/01/vote-button.jpg" />