Simple Gallery Slider - slider

I'm trying to create a simple slider using divs and javascript. I set up a div with six images and an arrow that movies the containder holding the images 528px (the width of each image) every time it's clicked. When I reach the begining or end of the gallery, I want the respective arrow button to fade out so that the user won't keep pressing next/prev.
Any help is appreciated.
JAVASCRIPT
$("#btn-gallery-next").click(function(){
$("div#gallery li").not(this).removeClass('clicked');
$("div#gallery-slide").animate({left:"-=528px"});
if($("div#gallery-slide").position().left < -3168)
{
$("#btn-gallery-next").fadeOut();
}
else {
$("#btn-gallery-next").fadeIn();
}
});
$("#btn-gallery-prev").click(function(){
$("div#gallery li").not(this).removeClass('clicked');
$("div#gallery-slide").animate({left:"+=528px"});
if($("div#gallery-slide").position().left > 0)
{
$("#btn-gallery-prev").fadeOut();
}
else {
$("#btn-gallery-prev").fadeIn();
}
});
HTML
<div id="gallery-slide">
<img class="gallery-img" src="_/img/gallery/img1.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img2.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img3.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img4.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img5.jpg" alt="" />
<img class="gallery-img" src="_/img/gallery/img6.jpg" alt="" />
</div>

Try flex slider from woothemes, it have all ur needs.

Why not use a slider library like Owl Slider? It comes with lots of options and configurations. It is super simple to integrate into any project.
Example #1 www.midwestgathering.com/#galleries
Example #2 www.owlgraphic.com/owlcarousel/demos/images.html
Another option is jcarousel. The basic slider is shown with an example that makes the left next button inactive until the user slides to the right, then once the user gets to the end of the gallery the right next button becomes inactive:
JS
(function($) {
$(function() {
$('.jcarousel').jcarousel();
$('.jcarousel-control-prev')
.on('jcarouselcontrol:active', function() {
$(this).removeClass('inactive');
})
.on('jcarouselcontrol:inactive', function() {
$(this).addClass('inactive');
})
.jcarouselControl({
target: '-=1'
});
$('.jcarousel-control-next')
.on('jcarouselcontrol:active', function() {
$(this).removeClass('inactive');
})
.on('jcarouselcontrol:inactive', function() {
$(this).addClass('inactive');
})
.jcarouselControl({
target: '+=1'
});
$('.jcarousel-pagination')
.on('jcarouselpagination:active', 'a', function() {
$(this).addClass('active');
})
.on('jcarouselpagination:inactive', 'a', function() {
$(this).removeClass('active');
})
.jcarouselPagination();
});
})(jQuery);
CSS
.jcarousel-wrapper {
margin: 20px auto;
position: relative;
border: 10px solid #fff;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
-webkit-box-shadow: 0 0 2px #999;
-moz-box-shadow: 0 0 2px #999;
box-shadow: 0 0 2px #999;
}
.jcarousel-wrapper .photo-credits {
position: absolute;
right: 15px;
bottom: 0;
font-size: 13px;
color: #fff;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.85);
opacity: .66;
}
.jcarousel-wrapper .photo-credits a {
color: #fff;
}
/** Carousel **/
.jcarousel {
position: relative;
overflow: hidden;
width: 600px;
height: 400px;
}
.jcarousel ul {
width: 20000em;
position: relative;
list-style: none;
margin: 0;
padding: 0;
}
.jcarousel li {
float: left;
}
/** Carousel Controls **/
.jcarousel-control-prev,
.jcarousel-control-next {
position: absolute;
top: 200px;
width: 30px;
height: 30px;
text-align: center;
background: #4E443C;
color: #fff;
text-decoration: none;
text-shadow: 0 0 1px #000;
font: 24px/27px Arial, sans-serif;
-webkit-border-radius: 30px;
-moz-border-radius: 30px;
border-radius: 30px;
-webkit-box-shadow: 0 0 2px #999;
-moz-box-shadow: 0 0 2px #999;
box-shadow: 0 0 2px #999;
}
.jcarousel-control-prev {
left: -50px;
}
.jcarousel-control-next {
right: -50px;
}
.jcarousel-control-prev:hover span,
.jcarousel-control-next:hover span {
display: block;
}
.jcarousel-control-prev.inactive,
.jcarousel-control-next.inactive {
opacity: .5;
cursor: default;
}
/** Carousel Pagination **/
.jcarousel-pagination {
position: absolute;
bottom: 0;
left: 15px;
}
.jcarousel-pagination a {
text-decoration: none;
display: inline-block;
font-size: 11px;
line-height: 14px;
min-width: 14px;
background: #fff;
color: #4E443C;
border-radius: 14px;
padding: 3px;
text-align: center;
margin-right: 2px;
opacity: .75;
}
.jcarousel-pagination a.active {
background: #4E443C;
color: #fff;
opacity: 1;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.75);
}
You can find documentation for jcarousel at www.sorgalla.com/jcarousel/docs/.

see demo - http://codepen.io/vsync/pen/waKju?editors=011
javascript
/**
* Slider - loops over images
* SEP 2014
* By - Yair Even-Or
*/
var Slider = function(elm, prev, next){
var that = this;
this.locked = false;
this.slider = elm;
this.children = this.slider.children;
this.itemWidth = this.children[0].clientWidth;
this.preloadImages();
next && next.addEventListener('click', function(){ that.move('next') });
prev && prev.addEventListener('click', function(){ that.move('prev') });
}
Slider.prototype = {
move : function(dir){
var that = this,
itemToMove;
if( this.locked ){
this.locked.removeAttribute('style');
this.slider.appendChild(this.locked);
clearTimeout(this.timer);
moveToEnd();
}
// do nothing if there are no items
if( this.children.length < 2 )
return false;
itemToMove = this.children[0];
this.locked = itemToMove;
if( dir == 'next' )
itemToMove.style.marginLeft = -this.itemWidth + 'px';
else{
itemToMove = this.children[this.children.length-1];
itemToMove.style.marginLeft = -this.itemWidth + 'px';
this.slider.insertBefore(itemToMove, this.children[0]);
setTimeout(function(){
itemToMove.removeAttribute('style');
},50);
this.preloadImages();
this.locked = false;
}
// move the child to the end of the items' list
if( dir == 'next' )
this.timer = setTimeout(moveToEnd, 420);
function moveToEnd(el){
itemToMove = el || itemToMove;
if( !itemToMove ) return;
itemToMove.removeAttribute('style');
that.slider.appendChild(itemToMove);
that.locked = false;
that.preloadImages();
}
},
preloadImages : function(){
this.lazyload(this.children[1].getElementsByTagName('img')[0] );
this.lazyload(this.children[this.children.length-1].getElementsByTagName('img')[0] );
},
// lazy load image
lazyload : function(img){
var lazy = img.getAttribute('data-src');
if( lazy ){
img.src = lazy;
img.removeAttribute('data-src');
}
}
}
// HOW TO USE /////////////////
var sliderElm = document.querySelector('.content'),
next = document.querySelector('.next'),
prev = document.querySelector('.prev'),
slider = new Slider(sliderElm, prev, next);
HTML (JADE syntax)
.slider
a.arrow.next
a.arrow.prev
ul.content
li
img(src='image1.jpg')
li
img(src='image2.jpg')
li
img(src='image3.jpg')
li
img(src='image4.jpg')

Related

With Swiper.js, navigation via bullets sometimes doesn't go to correct slide

I'm using swiper.js in my project and sometimes (maybe 1 out of 10 times), navigation by clicking the associated bullet does not work - it instead goes to the previous slide. My swiper config object does contain clickable: true for the pagination.
Check this code. I think someone had the same problem here
HTML
<h1>Swipe 2</h1>
<div id='slider' style='max-width:500px;margin:0 auto' class='swipe'>
<div class='swipe-wrap'>
<div><b>1</b></div>
<div><b>2</b></div>
<div><b>3</b></div>
</div>
</div>
<div class="counter content" style='text-align:center;padding-top:20px;'>
<ul id='position'>
<li class="on"></li>
<li ></li>
<li ></li>
</ul>
</div>
<div style='text-align:center;padding-top:20px;'>
<button onclick='slider.prev()'>prev</button>
<button onclick='slider.next()'>next</button>
</div>
<script src='swipe.js'></script>
CSS
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, del, dfn, em, img, ins, kbd, q, samp, small, strong, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, table, tbody, tfoot, thead, tr, th, td, article, aside, footer, header, nav, section {
margin:0;
padding:0;
border:0;
outline:0;
font-size:100%;
vertical-align:baseline;
background:transparent;
}
body {
-webkit-text-size-adjust:none;
font-family:sans-serif;
min-height:416px;
}
h1 {
font-size:33px;
margin:50px 0 15px;
text-align:center;
color:#212121;
}
h2 {
font-size:14px;
font-weight:bold;
color:#3c3c3c;
margin:20px 10px 10px;
}
small {
margin:0 10px 30px;
display:block;
font-size:12px;
}
a {
margin:0 0 0 10px;
font-size:12px;
color:#3c3c3c;
}
html, body {
background: #f3f3f3;
}
#console {
font-size: 12px;
font-family:"Inconsolata", "Monaco", "Consolas", "Andale Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
color: #999;
line-height: 18px;
margin-top: 20px;
max-height: 150px;
overflow: auto;
}
#slider div b {
display:block;
font-weight:bold;
color:#14ADE5;
font-size:20px;
text-align:center;
margin:10px;
padding:100px 10px;
box-shadow: 0 1px #EBEBEB;
background: #fff;
border-radius: 3px;
border: 1px solid;
border-color: #E5E5E5 #D3D3D3 #B9C1C6;
}
.swipe {
overflow: hidden;
visibility: hidden;
position: relative;
}
.swipe-wrap {
overflow: hidden;
position: relative;
}
.swipe-wrap > div {
float:left;
width:100%;
position: relative;
}
.counter {
height: 55px;
ul {
text-align: center;
li {
// padding: 5px;
display: inline-block;
height: 10px;
width: 10px;
border: solid 2px #404041;
border-radius: 10px;
margin: 2.5px;
&.on {
background: #404041;
}
}
}
}
JAVASCRIPT
var elem = document.getElementById('slider');
var slider =
Swipe(document.getElementById('slider'), {
auto: 10000,
continuous: true,
callback: function(pos) {
var i = bullets.length;
while (i--) {
bullets[i].className = ' ';
}
bullets[pos].className = 'on';
}
});
var bullets = document.getElementById('position').getElementsByTagName('li');
$('li').on('click', function(event){
event.preventDefault();
var index = $("li").index(event.currentTarget);
slider.slide(index);
});
// with jQuery
// window.mySwipe = $('#mySwipe').Swipe().data('Swipe');

vue.js, vuetify scroll event not firing when using css scroll snap

UPDATE dropped this approach and went with vue-awesome-swiper script
I"m been stuck on this for days. Basically I want to use css scroll snap and I want to monitor scroll also.
In this basic example with just javascript it works fine scroll event fires and div snaps with css. The other pen below with vue.js does not and that is my problem. Losing hair about this... any help appreciated!
https://codepen.io/travis-pancakes/pen/pGYOZK?editors=0011
var i = 0;
function Onscrollfnction(event) { document.getElementById("demo").innerHTML = i;
i = i + 1;
};
/* setup */
html, body, .holster {
height: 100%;
}
.holster {
display: flex;
align-items: center;
justify-content: space-between;
flex-flow: column nowrap;
font-family: monospace;
}
.container {
display: flex;
overflow: auto;
outline: 1px dashed lightgray;
flex: none;
}
.container.x {
width: 100%;
height: 128px;
flex-flow: row nowrap;
}
.container.y {
width: 256px;
height: 256px;
flex-flow: column nowrap;
}
/* scroll-snap */
.x.mandatory-scroll-snapping {
scroll-snap-type: x mandatory;
}
.y.mandatory-scroll-snapping {
scroll-snap-type: y mandatory;
}
.x.proximity-scroll-snapping {
scroll-snap-type: x proximity;
}
.y.proximity-scroll-snapping {
scroll-snap-type: y proximity;
}
.container > div {
text-align: center;
scroll-snap-align: center;
flex: none;
}
.x.container > div {
line-height: 128px;
font-size: 64px;
width: 100%;
height: 128px;
}
.y.container > div {
line-height: 256px;
font-size: 128px;
width: 256px;
height: 256px;
}
/* appearance fixes */
.y.container > div:first-child {
line-height: 1.3;
font-size: 64px;
}
/* coloration */
.container > div:nth-child(even) {
background-color: #87EA87;
}
.container > div:nth-child(odd) {
background-color: #87CCEA;
}
<div><p>Scrolled <span id="demo">0</span> times.</p></div>
<div class="container y mandatory-scroll-snapping" onscroll="Onscrollfnction();" dir="ltr">
<div>Y Mand. LTR</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
The vue.js, vuetify version does not
https://codepen.io/travis-pancakes/pen/BMbqPq?editors=1111
new Vue({
el: '#app',
data: function(){
return {
i: 0
}
},
created () {
},
methods: {
Onscrollfnction (event) {
document.getElementById("demo").innerHTML = this.i;
this.i = this.i + 1;
console.log('i ', i)
}
}
});
/* setup */
html, body, .holster {
height: 100%;
}
.holster {
display: flex;
align-items: center;
justify-content: space-between;
flex-flow: column nowrap;
font-family: monospace;
}
.container {
display: flex;
overflow: auto;
outline: 1px dashed lightgray;
flex: none;
}
.container.x {
width: 100%;
height: 128px;
flex-flow: row nowrap;
}
.container.y {
width: 256px;
height: 256px;
flex-flow: column nowrap;
}
/* scroll-snap */
.x.mandatory-scroll-snapping {
scroll-snap-type: x mandatory;
}
.y.mandatory-scroll-snapping {
scroll-snap-type: y mandatory;
}
.x.proximity-scroll-snapping {
scroll-snap-type: x proximity;
}
.y.proximity-scroll-snapping {
scroll-snap-type: y proximity;
}
.container > div {
text-align: center;
scroll-snap-align: center;
flex: none;
}
.x.container > div {
line-height: 128px;
font-size: 64px;
width: 100%;
height: 128px;
}
.y.container > div {
line-height: 256px;
font-size: 128px;
width: 256px;
height: 256px;
}
/* appearance fixes */
.y.container > div:first-child {
line-height: 1.3;
font-size: 64px;
}
/* coloration */
.container > div:nth-child(even) {
background-color: #87EA87;
}
.container > div:nth-child(odd) {
background-color: #87CCEA;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<!-- could use v-scroll="Onscrollfnction" with vuetify" --->
<div class="container y mandatory-scroll-snapping"
v-on:scroll.native="Onscrollfnction" dir="ltr">
<div>Y Mand. LTR</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
<p>Scrolled <span id="demo">0</span> times.</p>
</div>
</div>
vue-awesome-swiper does the functionality I'm going for

I would like to delay something after it's been clicked

I'm creating a multiple choice question program and I have it so that once the correct answer is chosen a new question is generated.
Although I want it to go to the next question automatically, I want it to delay for about 0.5 seconds so the user can see that their answer is correct.
I chose to remove the class and replace it with another class so that the background changes colour. Once the new question comes up I want all the colours to return to normal, so I once again remove the new class and replace it with the old class.
If I don't advance to the new question automatically, the colours come up just the way I like them, but if I create it so that it moves on automatically, I am unable to keep the display the same.
After searching through the forums I read that setTimeout should work, but I haven't had much success. I have also tried doing animations so that it takes time, but that didn't work for me either. The animations worked fine, but it still went on to the new set of questions.
I'll include the whole program as it might be better but the section I'm working on is under the function check().
I've been trying to figure out how to delay something for a long time but have been completely unsuccessful. Oh, please be kind to me, I have very little experience. I have only learned how to do javascript by doing the khan academy course. Thanks!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Project: listening to sounds </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- ***************** CSS styles ***************** -->
<style>
body {
font-family: comic sans ms, sans-serif;
background-image: url("background.jpg");
background-color: rgb(216, 252, 252);
}
form {
font-size: 1.2em;
}
#text {
background-color: wheat;
width: 150px;
height: 25px;
color: blue;
font-size: 1em;
}
.SoundBite {
float: left;
clear: none;
position: absolute;
top: 130px;
left: 100px;
padding: 5px;
background: darkblue;
color: white;
height: 120px;
width: 200px;
border-radius: 50px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 5em;
}
.SoundBite:hover {
background-color: darkgreen;
cursor: pointer;
}
.Score {
float: right;
clear: none;
position: absolute;
top: 50px;
left: 140px;
padding: 5px;
background: darkblue;
border-color: pink;
border: 5px;
opacity: 0.8;
color: white;
height: 45px;
width: 120px;
border-radius: 50px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
pointer-events: none;
}
.Answer {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: darkblue;
}
.AnswerCorrect {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: green;
}
.Answer:hover {
background-color: #e44404;
cursor: pointer;
}
.AnswerWrong {
position: absolute;
color: white;
height: 45px;
width: 120px;
padding: 5px;
border-radius: 25px;
margin: auto;
text-align: center;
vertical-align: middle;
font-size: 2em;
background-color: red;
}
#Answer1 {
top: 20px;
left: 400px;
}
#Answer2 {
top: 85px;
left: 400px;
}
#Answer3 {
top: 150px;
left: 400px;
}
#Answer4 {
top: 220px;
left: 400px;
}
#Answer5 {
top: 290px;
left: 400px;
}
</style>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<body>
<audio id="audio" src="audio/rug.mp3" autostart="false" ></audio>
<audio id = "win" src="audio/win.mp3" autostart = "true"></audio>
<audio id = "lose" src = "audio/lose.mp3" autstart = "true"></audio>
<a onclick="playSound();"><div span class="SoundBite"><i class="fa fa-file-sound-o" id="audio" src="audio/rug.mp3" autostart="false" style="font-size:64px;color:skyblue"></i></div></a>
<a onclick ="checkAnswer = 1; check();"><div class = "Answer" id="Answer1">1</div>
<a onclick ="checkAnswer = 2; check();"><div class = "Answer" id="Answer2">2</div>
<a onclick ="checkAnswer = 3; check();"><div class = "Answer" id="Answer3">3</div>
<a onclick ="checkAnswer = 4; check();"><div class = "Answer" id="Answer4">4</div>
<a onclick ="checkAnswer = 5; check();"><div class = "Answer" id="Answer5">5</div>
<div class = "Score">Score</div>
<!-- <a onclick ="next();"><div id = "Next">Start</div> -->
<!-- ********************************* Javascript programming follows ********************************* -->
<script>
$(function() {
});
// declare the variables to be used ... do I need global variables? Maybe I should think about these...
var word, ext, directory, wordPosition, decoyWordPosition, answerPosition, decoyAnswerPosition, answer, checkAnswer, correct, incorrect, tries;
directory = "audio/";
ext = ".mp3";
correct = 0;
incorrect = 0;
tries = 0;
// list of words that are spoken
word = ["dam", "dog", "dug", "cat", "cot", "cut", "ran", "rot", "rug"];
wordPosition = Math.floor(Math.random()*word.length); // returns a random array wordPosition
/***************************************************************************
** Functions:
**
**************************************************************************/
// function to display word
function displayWord() {
//$(".SoundBite").text(word[wordPosition]);
//$("#audio").attr("src", directory+word[wordPosition]+ext);
}
// function to display the answer in one of the positions that are assigned
function displayAnswer(answerNumber, wordNumber) {
$("#Answer"+answerNumber).text(word[wordNumber]);
$("#audio").attr("src", directory+word[wordPosition]+ext);
}
// function to play the sound
function playSound() {
var sound = document.getElementById("audio");
sound.play();
}
// function to play a winning sound
function win() {
var sound = document.getElementById("win");
sound.play();
}
// function to play a losing sound
function lose() {
var sound = document.getElementById("lose");
sound.play();
}
function timer() {
new Date().toLocaleTimeString();
}
function check() {
if (answer == checkAnswer) {
//$("#Answer"+checkAnswer).animate({height: "45px", opacity: '0'})
//.animate({height: "45px", opacity: '1.0'});
$("#Answer"+checkAnswer).animate({height: "45px", opacity: '0'})
.animate({height: "45px", opacity: '1.0'})
.removeClass("Answer").addClass("AnswerCorrect");
win();
correct++;
tries++;
refresh();
next();
// need a delay function -- can't get it to work.
} else {
$("#Answer"+checkAnswer).removeClass("Answer").addClass("AnswerWrong"); //css({'background-color': 'red'});
lose();
incorrect++;
tries++;
}
// write in the score
$(".Score").text(Math.round(correct/tries*100)+"%");
}
// making a function to populate the answers
function populateAnswers() {
for (var i = 0; i < 6; i++) {
if (answerPosition < 6) {
if (decoyWordPosition == wordPosition) {
answer=answerPosition
}
displayAnswer(answerPosition, decoyWordPosition);
answerPosition++;
decoyWordPosition++;
if (decoyWordPosition >= word.length) { // want to make sure that the words are within the array
decoyWordPosition = 1 // reset to the beginning to 'wrap' the array.
}
} else {
answerPosition = 1;
}
}
}
// using a random generator to place the answer in a random spot 1 through 4
function randomGenerator() {
answerPosition = Math.floor(Math.random()*4)+1;
}
// returns a random array wordPosition
function randomWord() {
wordPosition = Math.floor(Math.random()*word.length);
}
function refresh() {
for (var i = 1; i <= 5; i++) {
$("#Answer"+i).removeClass("AnswerWrong").addClass("Answer");
$("#Answer"+i).removeClass("AnswerCorrect").addClass("Answer");
}
}
/***************************************************************************
* program as a function *
**************************************************************************/
function next() {
$("#Next").text("Continue");
randomWord();
randomGenerator();
// make the decoy answers randomly
if (wordPosition == 0 || wordPosition == 1) {
// In case the array is at the beginning: make the decoy start at the same spot as the wordPosition
decoyWordPosition = wordPosition;
} else {
// start the decoy word after the word
decoyWordPosition = wordPosition -2;
}
populateAnswers();
}
next();
</script>
</body>
</html>
Just for other people who are trying to find the solution to the setTimeout feature.
I was using it like this:
setTimeout(myFunction(), 2000);
However, it won't work with the brackets after the function. You need to omit those brackets:
setTimeout(myFunction, 2000);

How to integrate jsplumb in vuejs?

I have tried jsplumb script in .html file under the script tag. This is working fine.
<body>
<div id="q-app"></div>
<!-- built files will be auto injected -->
<script>
jsPlumb.ready(function() {
jsPlumb.connect({
source:"item_left",
target:"item_right",
endpoint:"Rectangle"
});
jsPlumb.draggable("item_left");
jsPlumb.draggable("item_right");
});
</script>
</body>
But now i want to integrate this jsplumb code/script in .vue file.I tried to put this script in .vue file under script tag, but i did not get any output except an blank page with zero errors. How can i proceed further?.Guide me with some simple example.
You can try this:
mydraggableview.vue
<template>
<div id="diagramContainer">
<div id="item_left" class="item"></div>
<div id="item_right" class="item" style="margin-left:50px;"></div>
</div>
</template>
<script>
import jsplumb from 'jsplumb'
export default {
props: ['yourProps'],
data() {
return {
your: data
}
},
mounted(){
jsPlumb.ready(function() {
jsPlumb.connect({
source:"item_left",
target:"item_right",
endpoint:"Rectangle"
})
})
}
}
</script>
Then you can import where you are going to use it.
otherfile.js
<script>
import myDraggableComponent from './path/to/component/mydraggableview'
</script>
and use it as directive or inside your component.
I managed to put together working code based on jsplumb demo community example -
<template>
<div id="canvas" class="jtk-demo-canvas canvas-wide flowchart-demo jtk-surface jtk-surface-nopan">
<div id="flowchartWindow1" class="window jtk-node">1</div>
<div id="flowchartWindow2" class="window jtk-node">2</div>
<div id="flowchartWindow3" class="window jtk-node">3</div>
<div id="flowchartWindow4" class="window jtk-node">4</div>
</div>
</template>
<script>
import { jsPlumb as JSPlumb } from 'jsplumb'
export default {
name: 'JsPlumb',
data () {
return {
}
},
mounted () {
JSPlumb.ready(function() {
var instance = window.jsp = JSPlumb.getInstance({
// default drag options
DragOptions: { cursor: 'pointer', zIndex: 2000 },
// the overlays to decorate each connection with. note that the label overlay uses a function to generate the label text; in this
// case it returns the 'labelText' member that we set on each connection in the 'init' method below.
ConnectionOverlays: [
[ "Arrow", {
location: 1,
visible:true,
width:11,
length:11,
id:"ARROW",
events:{
click:function() { alert("you clicked on the arrow overlay")}
}
} ],
[ "Label", {
location: 0.1,
id: "label",
cssClass: "aLabel",
events:{
tap:function() { alert("hey"); }
}
}]
],
Container: "canvas"
});
var basicType = {
connector: "StateMachine",
paintStyle: { stroke: "red", strokeWidth: 4 },
hoverPaintStyle: { stroke: "blue" },
overlays: [
"Arrow"
]
};
instance.registerConnectionType("basic", basicType);
// this is the paint style for the connecting lines..
var connectorPaintStyle = {
strokeWidth: 2,
stroke: "#61B7CF",
joinstyle: "round",
outlineStroke: "white",
outlineWidth: 2
},
// .. and this is the hover style.
connectorHoverStyle = {
strokeWidth: 3,
stroke: "#216477",
outlineWidth: 5,
outlineStroke: "white"
},
endpointHoverStyle = {
fill: "#216477",
stroke: "#216477"
},
// the definition of source endpoints (the small blue ones)
sourceEndpoint = {
endpoint: "Dot",
paintStyle: {
stroke: "#7AB02C",
fill: "transparent",
radius: 7,
strokeWidth: 1
},
isSource: true,
connector: [ "Flowchart", { stub: [40, 60], gap: 10, cornerRadius: 5, alwaysRespectStubs: true } ],
connectorStyle: connectorPaintStyle,
hoverPaintStyle: endpointHoverStyle,
connectorHoverStyle: connectorHoverStyle,
dragOptions: {},
overlays: [
[ "Label", {
location: [0.5, 1.5],
label: "Drag",
cssClass: "endpointSourceLabel",
visible:false
} ]
]
},
// the definition of target endpoints (will appear when the user drags a connection)
targetEndpoint = {
endpoint: "Dot",
paintStyle: { fill: "#7AB02C", radius: 7 },
hoverPaintStyle: endpointHoverStyle,
maxConnections: -1,
dropOptions: { hoverClass: "hover", activeClass: "active" },
isTarget: true,
overlays: [
[ "Label", { location: [0.5, -0.5], label: "Drop", cssClass: "endpointTargetLabel", visible:false } ]
]
},
init = function (connection) {
connection.getOverlay("label").setLabel(connection.sourceId.substring(15) + "-" + connection.targetId.substring(15));
};
var _addEndpoints = function (toId, sourceAnchors, targetAnchors) {
for (var i = 0; i < sourceAnchors.length; i++) {
var sourceUUID = toId + sourceAnchors[i];
instance.addEndpoint("flowchart" + toId, sourceEndpoint, {
anchor: sourceAnchors[i], uuid: sourceUUID
});
}
for (var j = 0; j < targetAnchors.length; j++) {
var targetUUID = toId + targetAnchors[j];
instance.addEndpoint("flowchart" + toId, targetEndpoint, { anchor: targetAnchors[j], uuid: targetUUID });
}
};
// suspend drawing and initialise.
instance.batch(function () {
_addEndpoints("Window4", ["TopCenter", "BottomCenter"], ["LeftMiddle", "RightMiddle"]);
_addEndpoints("Window2", ["LeftMiddle", "BottomCenter"], ["TopCenter", "RightMiddle"]);
_addEndpoints("Window3", ["RightMiddle", "BottomCenter"], ["LeftMiddle", "TopCenter"]);
_addEndpoints("Window1", ["LeftMiddle", "RightMiddle"], ["TopCenter", "BottomCenter"]);
// listen for new connections; initialise them the same way we initialise the connections at startup.
instance.bind("connection", function (connInfo, originalEvent) {
init(connInfo.connection);
});
// make all the window divs draggable
instance.draggable(JSPlumb.getSelector(".flowchart-demo .window"), { grid: [20, 20] });
// THIS DEMO ONLY USES getSelector FOR CONVENIENCE. Use your library's appropriate selector
// method, or document.querySelectorAll:
//JSPlumb.draggable(document.querySelectorAll(".window"), { grid: [20, 20] });
// connect a few up
instance.connect({uuids: ["Window2BottomCenter", "Window3TopCenter"]});
instance.connect({uuids: ["Window2LeftMiddle", "Window4LeftMiddle"]});
instance.connect({uuids: ["Window4TopCenter", "Window4RightMiddle"]});
instance.connect({uuids: ["Window3RightMiddle", "Window2RightMiddle"]});
instance.connect({uuids: ["Window4BottomCenter", "Window1TopCenter"]});
instance.connect({uuids: ["Window3BottomCenter", "Window1BottomCenter"] });
//
//
// listen for clicks on connections, and offer to delete connections on click.
//
instance.bind("click", function (conn, originalEvent) {
// if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
// instance.detach(conn);
conn.toggleType("basic");
});
instance.bind("connectionDrag", function (connection) {
console.log("connection " + connection.id + " is being dragged. suspendedElement is ", connection.suspendedElement, " of type ", connection.suspendedElementType);
});
instance.bind("connectionDragStop", function (connection) {
console.log("connection " + connection.id + " was dragged");
});
instance.bind("connectionMoved", function (params) {
console.log("connection " + params.connection.id + " was moved");
});
});
JSPlumb.fire("jsPlumbDemoLoaded", instance);
})
}
}
</script>
<style>
.item{
height:50px;
width:50px;
background-color: red;
display: inline-block;
}
.demo {
/* for IE10+ touch devices */
touch-action:none;
}
.flowchart-demo .window {
border: 1px solid #346789;
box-shadow: 2px 2px 19px #aaa;
-o-box-shadow: 2px 2px 19px #aaa;
-webkit-box-shadow: 2px 2px 19px #aaa;
-moz-box-shadow: 2px 2px 19px #aaa;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
opacity: 0.8;
width: 80px;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
text-align: center;
z-index: 20;
position: absolute;
background-color: #eeeeef;
color: black;
font-family: helvetica, sans-serif;
padding: 0.5em;
font-size: 0.9em;
-webkit-transition: -webkit-box-shadow 0.15s ease-in;
-moz-transition: -moz-box-shadow 0.15s ease-in;
-o-transition: -o-box-shadow 0.15s ease-in;
transition: box-shadow 0.15s ease-in;
}
.flowchart-demo .window:hover {
box-shadow: 2px 2px 19px #444;
-o-box-shadow: 2px 2px 19px #444;
-webkit-box-shadow: 2px 2px 19px #444;
-moz-box-shadow: 2px 2px 19px #444;
opacity: 0.6;
}
.flowchart-demo .active {
border: 1px dotted green;
}
.flowchart-demo .hover {
border: 1px dotted red;
}
#flowchartWindow1 {
top: 34em;
left: 5em;
}
#flowchartWindow2 {
top: 7em;
left: 36em;
}
#flowchartWindow3 {
top: 27em;
left: 48em;
}
#flowchartWindow4 {
top: 23em;
left: 22em;
}
.flowchart-demo .jtk-connector {
z-index: 4;
}
.flowchart-demo .jtk-endpoint, .endpointTargetLabel, .endpointSourceLabel {
z-index: 21;
cursor: pointer;
}
.flowchart-demo .aLabel {
background-color: white;
padding: 0.4em;
font: 12px sans-serif;
color: #444;
z-index: 21;
border: 1px dotted gray;
opacity: 0.8;
cursor: pointer;
}
.flowchart-demo .aLabel.jtk-hover {
background-color: #5C96BC;
color: white;
border: 1px solid white;
}
.window.jtk-connected {
border: 1px solid green;
}
.jtk-drag {
outline: 4px solid pink !important;
}
path, .jtk-endpoint {
cursor: pointer;
}
.jtk-overlay {
background-color:transparent;
}
/* ---------------------------------------------------------------------------------------------------- */
/* --- page structure --------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------- */
body {
background-color: #FFF;
color: #434343;
font-family: "Lato", sans-serif;
font-size: 14px;
font-weight: 400;
height: 100%;
padding: 0;
}
.jtk-bootstrap {
min-height:100vh;
display:flex;
flex-direction: column;
}
.jtk-bootstrap .jtk-page-container {
display:flex;
width:100vw;
justify-content: center;
flex:1;
}
.jtk-bootstrap .jtk-container {
width: 60%;
max-width:800px;
}
.jtk-bootstrap-wide .jtk-container {
width: 80%;
max-width:1187px;
}
.jtk-demo-main {
position: relative;
margin-top:98px;
display:flex;
flex-direction:column;
}
.jtk-demo-main .description {
font-size: 13px;
margin-top: 25px;
padding: 13px;
margin-bottom: 22px;
background-color: #f4f5ef;
}
.jtk-demo-main .description li {
list-style-type: disc !important;
}
.jtk-demo-canvas {
height:750px;
max-height:700px;
border:1px solid #CCC;
background-color:white;
display: flex;
flex-grow:1;
}
.canvas-wide {
margin-left:0;
}
.miniview {
position: absolute;
top: 25px;
right: 25px;
z-index: 100;
}
.jtk-demo-dataset {
text-align: left;
max-height: 600px;
overflow: auto;
}
.demo-title {
float:left;
font-size:18px;
}
.controls {
top: 25px;
color: #FFF;
margin-right: 10px;
position: absolute;
left: 25px;
z-index: 1;
display:flex;
}
.controls i {
background-color: #5184a0;
border-radius: 4px;
cursor: pointer;
margin-right: 4px;
padding: 4px;
}
li {
list-style-type: none;
}
/* ------------------------ node palette -------------------- */
.sidebar {
margin:0;
padding:10px 0;
background-color: white;
display:flex;
flex-direction:column;
border: 1px solid #CCC;
align-items: center;
}
.sidebar-item {
background-color: #CCC;
border-radius: 11px;
color: #585858;
cursor: move;
padding: 8px;
width: 128px;
text-align: center;
margin: 10px;
outline:none;
}
button.sidebar-item {
cursor:pointer;
width:150px;
}
.sidebar select {
height:35px;
width:150px;
outline:none;
}
.sidebar-item.katavorio-clone-drag {
margin:0;
border:1px solid white;
}
.sidebar-item:hover, .sidebar-item.katavorio-clone-drag {
background-color: #5184a0;
color:white;
}
/*
.sidebar button {
background-color: #30686d;
outline: none;
border: none;
margin-left: 25px;
padding: 7px;
color: white;
cursor:pointer;
}*/
.sidebar i {
float:left;
}
#media (max-width: 600px) {
.sidebar {
float:none;
height: 55px;
width: 100%;
padding-top:0;
}
.sidebar ul li {
display:inline-block;
margin-top: 7px;
width:67px;
}
.jtk-demo-canvas {
margin-left: 0;
margin-top:10px;
height:364px;
}
}
/* ---------------------------------------------------------------------------------------------------- */
/* --- jsPlumb setup ---------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------- */
.jtk-surface-pan {
display:none;
}
.jtk-connector {
z-index:9;
}
.jtk-connector:hover, .jtk-connector.jtk-hover {
z-index:10;
}
.jtk-endpoint {
z-index:12;
opacity:0.8;
cursor:pointer;
}
.jtk-overlay {
background-color: white;
color: #434343;
font-weight: 400;
padding: 4px;
z-index:10;
}
.jtk-overlay.jtk-hover {
color: #434343;
}
path {
cursor:pointer;
}
.delete {
padding: 2px;
cursor: pointer;
float: left;
font-size: 10px;
line-height: 20px;
}
.add, .edit {
cursor: pointer;
float:right;
font-size: 10px;
line-height: 20px;
margin-right:2px;
padding: 2px;
}
.edit:hover {
color: #ff8000;
}
.selected-mode {
color:#E4F013;
}
.connect {
width:10px;
height:10px;
background-color:#f76258;
position:absolute;
bottom: 13px;
right: 5px;
}
/* header styles */
.demo-links {
position: fixed;
right: 0;
top: 57px;
font-size: 11px;
background-color: white;
opacity: 0.8;
padding-right: 10px;
padding-left: 5px;
text-transform: uppercase;
z-index:100001;
}
.demo-links div {
display:inline;
margin-right:7px;
margin-left:7px;
}
.demo-links i {
padding:4px;
}
.jtk-node {
background-color: #5184a0;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
position: absolute;
z-index: 11;
overflow: hidden;
min-width:80px;
min-height:30px;
width: auto;
}
.jtk-node .name {
color: white;
cursor: move;
font-size: 13px;
line-height: 24px;
padding: 6px;
text-align: center;
}
.jtk-node .name span {
cursor:pointer;
}
[undo], [redo] { background-color:darkgray !important; }
[can-undo='true'] [undo], [can-redo='true'] [redo] { background-color: #3E7E9C !important; }
</style>
See the official article to integrate with VueJs:
https://docs.jsplumbtoolkit.com/toolkit/current/articles/demo-vue2.html

CSS TranslationX of container not working in Safari

Today I have noticed a weird behavior of Safari (9.0) when I applied a transition to an element that was translating on the X axis while the width was also increasing.
I have reproduced the behavior in this JsFiddle. Here is an embed code for those who like it better. In Firefox and Chrome it looks pretty smooth but not in Safari, does anyone have a solution or a best way to achieve the same effect?
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() { container.classList.toggle('open'); });
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, transform 1s;
}
.open ul {
width: 200%;
transform: translateX(-50%);
}
li {
/* Just some style first */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
padding: 1em 0;
display: inline-block;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Re-posting as an answer.
Here is the jsFiddle result and snippet as below:
var button = document.getElementsByTagName('button')[0];
var container = document.getElementsByTagName('div')[0];
var timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50, ease: Power2.easeInOut });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Hope this is helpful.
P.S. I have been using GSAP for quite a while now and I don't remember getting stuck on any browser-specific issues unless a browser would do something differently. A little research into GSAP and it would tell you that browser compatibility is one of their main selling points.
By animating margin-left instead of translateX the result is acceptable in Safari:
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: width 1s, margin-left 1s;
}
.open ul {
width: 200%;
margin-left:-100%;
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
Using scaleX instead of animating width is smoother, but probably not what you want.
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
});
.container {
width: 100%;
overflow: hidden;
}
ul {
display: block;
width: 100%;
padding: 0;
margin: 0;
transition: transform 1s;
}
.open ul {
transform: translateX(-50%) scaleX(2);
}
li {
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
background-color: red;
display: inline-block;
padding: 1em 0;
width: calc(50% - 4px);
}
li:first-child {
background-color: green;
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
So, I will try to sum up the two best solutions here : one with CSS transform and the other with Javascript animation (GSAP).
CSS TRANSFORM
In terms of performance, it is recommended to only animate transforms (translate, scale, rotate) and opacity. If you are interested in more optimisation details you can have a look at this article by Anna Migas.
So, as #Meiko suggested, the best solution is to only animate scale and translate properties. Here is a code sample (and the JSFiddle)
var button = document.getElementsByTagName('button')[0],
container = document.getElementsByTagName('div')[0];
button.addEventListener('click', function() {
container.classList.toggle('open');
})
.container,
ul {
width: 100%;
}
ul {
overflow: hidden;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
position: relative;
transition: transform 1s;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li::before {
content: '';
position: absolute;
top: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
background-color: red;
z-index: -1;
transition: transform 1s;
}
li:first-child::before {
background-color: green;
}
.open li:first-child {
transform: translateX(-100%);
}
.open li:nth-of-type(2) {
transform: translateX(-50%);
}
.open li:nth-of-type(2)::before {
transform: scaleX(2);
}
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Only use a tiny bit of Javascript to toggle class,
The browser support is quite good (needs vendor-specific properties and some testing),
Really fast and light on GPU memory.
CONS:
Pretty limited in terms of usage (the actual width of the second cell stays the same),
Needs more lines of CSS.
JS ANIMATION (WITH GSAP)
This solution has been suggested by #Tahir Ahmed and use the GSAP library. As a side note, I really think that this is the best js library out there for this kind of animation. Here is a snippet of how it works (and the JSFiddle):
var button = document.getElementsByTagName('button')[0],
timeline = new TimelineMax({ paused: true });
timeline.to('ul', 1, { width: '200%', xPercent: -50 });
button.addEventListener('click', function() {
timeline.progress() > 0 ? timeline.reverse() : timeline.play();
})
.container {
width: 100%;
overflow: hidden;
}
ul {
width: 100%;
/* reset default browser styles */
padding: 0;
margin: 0;
}
li {
display: inline-block;
width: calc(50% - 2px);
background-color: red;
/* Just some style */
font-family: sans-serif;
color: #fff;
text-align: center;
text-transform: uppercase;
padding: 1em 0;
}
li:first-child {
background-color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.2/TweenMax.min.js"></script>
<div class="container">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
<button type="button">Toggle translation</button>
PROS:
Really flexible, sky is the limit!
You can animate properties such as display (you can't in CSS),
Compatible with every browser out there (down to IE6).
CONS:
Require a third party library (about 30kb),
Seems a bit harder for the GPU (although it needs more testing to be sure).
In the end it really depends on the animation you need but if it get's a little bit more complex than moving a container around then I will choose GSAP.