Pointer Events API and dragging - javascript

I'm trying to implement mobile-only touch UI using Pointer Events API and this just isn't working:
https://codepen.io/kyrsquir/full/gOrZEoe
The goal is to allow scrolling in the card and dragging the card between expanded and collapsed state within the same UI. I only managed to get dragging by the handle to work in Chrome emulator and iOS Safari. Dragging via card content doesn't work anywhere.
On Android Chrome dragging doesn't work at all.
The weirdest part is that the same UI works everywhere using touchstart, touchmove and touchend events (https://codepen.io/kyrsquir/full/QWNZzav) but performance of touchmove in Chrome on Android in the presence of scrollbar is so terrible that I'm trying to reimplement it with pointer events.
Vue component code:
<template>
<div class="container">
<div
:class="containerClass"
:style="containerStyle"
#pointerdown="pointerDownHandler"
#pointermove="pointerMoveHandler"
#pointerup="pointerUpHandler"
#pointerover="reportEvent"
#pointerenter="reportEvent"
#pointercancel="reportEvent"
#pointerout="reportEvent"
#pointerleave="reportEvent"
#gotpointercapture="reportEvent"
#lostpointercapture="reportEvent"
#transitionend="transitionEndHandler"
class="card"
>
<svg viewBox="0 0 100 20" always-swipeable="true" class="drag-handle">
<polyline points="27,10 73,10" stroke-linecap="round"></polyline>
</svg>
<div class="scrollbox" ref="scrollbox">
<div :key="item" class="item" v-for="item in items">
{{ item }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
deltaY: 0,
isDraggingByHandle: false,
isTransitioning: false,
items: [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
],
offsetHeight: 0,
scrollboxHeight: 0,
scrollTop: 0,
touchAction: null,
verticalStates: [
{
translateY: 0,
},
{
translateY: window.innerHeight - 200,
},
],
verticalStateIndex: 0,
};
},
computed: {
activeVerticalState() {
return this.verticalStates[this.verticalStateIndex];
},
containerClass() {
return {
"show-scrollbar": this.isScrollable,
transition: this.isTransitioning,
};
},
containerStyle() {
return {
transform: `translateY(${this.translateY}px)`,
};
},
isAnySwipeAllowed() {
return this.isDraggingByHandle || !this.isScrollable;
},
isExpanded() {
return this.verticalStateIndex === 0;
},
isScrollable() {
return this.isExpanded && this.scrollHeight > this.offsetHeight;
},
isSwipeDown() {
return (
this.deltaY > 0 && (this.isAnySwipeAllowed || this.scrollTop === 0)
);
},
isSwipeUp() {
return (
this.deltaY < 0 &&
(this.isAnySwipeAllowed ||
this.offsetHeight + this.scrollTop === this.scrollHeight)
);
},
scrollbox() {
return this.$refs.scrollbox;
},
translateY() {
let translateY = this.activeVerticalState.translateY;
if (
this.touchAction === "verticalSwipe" &&
(this.isSwipeDown || this.isSwipeUp)
) {
translateY = translateY + this.deltaY;
}
return translateY;
},
},
mounted() {
this.updateScrollboxData();
},
methods: {
pointerDownHandler: function ({ clientY, target, pointerId }) {
console.log("pointerdown", target, pointerId);
target.setPointerCapture(pointerId);
this.updateScrollboxData();
this.isDraggingByHandle = Boolean(
target.getAttribute("always-swipeable")
);
this.touchStartClientY = clientY;
this.touchAction = "tap";
},
pointerMoveHandler: function ({ clientY, target, pointerId }) {
console.log("pointermove", target, pointerId, this.deltaY);
this.deltaY = clientY - this.touchStartClientY;
// promote touchAction to swipe or scroll depending on deltas and other variables
if (this.touchAction === "tap") {
if (this.isSwipeDown || this.isSwipeUp) {
this.touchAction = "verticalSwipe";
} else {
this.touchAction = "scroll";
this.updateScrollboxData();
}
}
},
pointerUpHandler: function ({ target, pointerId }) {
console.log("pointerup", target, pointerId);
target.releasePointerCapture(pointerId);
switch (this.touchAction) {
case "tap":
if (!this.isExpanded) {
this.verticalStateIndex = Math.max(this.verticalStateIndex - 1, 0);
this.isTransitioning = true;
}
break;
case "verticalSwipe":
if (this.isSwipeDown) {
this.verticalStateIndex = Math.min(
this.verticalStateIndex + 1,
this.verticalStates.length - 1
);
} else if (this.isSwipeUp) {
this.verticalStateIndex = Math.max(this.verticalStateIndex - 1, 0);
}
this.isTransitioning = true;
this.deltaY = 0;
break;
}
},
reportEvent({ type, target, pointerId }) {
console.log(type, target, pointerId);
},
transitionEndHandler() {
this.touchAction = null;
this.isTransitioning = false;
this.updateScrollboxData();
},
updateScrollboxData() {
const { scrollHeight, offsetHeight, scrollTop } = this.scrollbox;
this.offsetHeight = offsetHeight;
this.scrollHeight = scrollHeight;
this.scrollTop = scrollTop;
},
},
};
</script>
<style lang="scss" scoped>
.container {
display: flex;
justify-content: center;
margin-top: 5vh;
overflow: hidden;
touch-action: none;
.card {
pointer-events: all;
width: 90vw;
height: 100%;
touch-action: none;
&.transition {
transition: transform 0.15s ease-out;
}
.drag-handle {
stroke-width: 5px;
stroke: #bfbfc0;
width: 100%;
height: 30px;
background: pink;
}
.scrollbox {
overflow-y: hidden;
pointer-events: none;
background: green;
height: 85vh;
.item {
margin-bottom: 20px;
height: 150px;
font-size: 36px;
background: yellow;
}
}
&.show-scrollbar .scrollbox {
overflow-y: scroll;
pointer-events: all;
}
}
}
</style>

Related

Nuxt js Custom cursor event listener not working after route change

I'm currently building a new website for our studio but can't get the custom cursor to work properly. Here's a custom cursor built with gsap and the result was great except when I navigate to another route and back to the home page, the mouseover event stops working and I can't find a reason why.
What could be causing this and how could this be fixed?
Thank you in advance!
Here's the CustomCursor component:
<template>
<div class="custom-cursor">
<div id="cursor-big" class="custom-cursor__ball custom-cursor__ball--big"></div>
<div id="cursor-small" class="custom-cursor__ball custom-cursor__ball--small"></div>
</div>
</template>
<script>
import gsap from "gsap";
export default {
props: {
hoverClass: {
type: String,
default: 'cursorHover'
}
},
mounted () {
const cursorBig = document.getElementById('cursor-big'),
cursorSmall = document.getElementById('cursor-small'),
links = document.getElementsByTagName("a"),
withClassHover = document.getElementsByClassName(this.hoverClass),
withHover = [...links, ...withClassHover];
// Event Listeners
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mousedown", onMouseHover);
document.addEventListener("mouseup", onMouseHoverOut);
document.addEventListener("mouseenter", () => {
cursorBig.style.opacity = 1;
cursorSmall.style.opacity = 1;
});
document.addEventListener("mouseleave", () => {
cursorBig.style.opacity = 0;
cursorSmall.style.opacity = 0;
});
withHover.forEach((element) => {
element.addEventListener("mouseover", onMouseHover);
element.addEventListener("mouseout", onMouseHoverOut);
})
// Event Handlers
function onMouseMove(e) {
cursorSmall.style.opacity = 1;
gsap.to(cursorBig, 0.4, {
x: e.clientX - 18.5,
y: e.clientY - 18.5
});
gsap.to(cursorSmall, 0.1, {
x: e.clientX - 4,
y: e.clientY - 4
});
}
function onMouseHover() {
gsap.to(cursorBig, 0.3, {
scale: 3,
});
}
function onMouseHoverOut() {
gsap.to(cursorBig, 0.3, {
scale: 1,
});
}
}
};
</script>
<style>
#media screen and (min-width:1100px) {
* {
cursor: none !important;
}
.custom-cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 99999;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease;
}
.custom-cursor__ball--big {
content: "";
width: 35px;
height: 35px;
background: white;
border-radius: 50%;
}
.custom-cursor__ball--small {
content: "";
width: 6px;
height: 6px;
background: #fff;
border-radius: 50%;
}
}
</style>
Moved from comments:
Issue: Elements with cursorHover class are not on the DOM after they have been removed when you route off somewhere else. Mounted only fires once.
Fix: Handle reinitiating your events onto the dom elements, and destroy your custom event handlers as the route changes.
<template>
<div class="custom-cursor">
<div
id="cursor-big"
class="custom-cursor__ball custom-cursor__ball--big"
></div>
<div
id="cursor-small"
class="custom-cursor__ball custom-cursor__ball--small"
></div>
</div>
</template>
<script>
import gsap from "gsap";
export default {
name: "CustomCursor",
props: {
hoverClass: {
type: String,
default: "cursorHover",
},
},
data() {
return {
cursorBig: null,
cursorSmall: null,
withHover: [],
};
},
watch: {
"$route.path"() {
console.log("route change");
this.destroy();
this.$nextTick(this.init);
},
},
mounted() {
console.log("mounted");
this.$nextTick(this.init);
},
beforeDestroy() {
console.log("beforeDestroy");
this.destroy();
},
methods: {
init() {
console.log("init");
setTimeout(() => {
this.cursorBig = document.getElementById("cursor-big");
this.cursorSmall = document.getElementById("cursor-small");
this.withHover = [
...document.getElementsByTagName("a"),
...document.getElementsByClassName(this.hoverClass),
];
this.withHover.forEach((element) => {
element.addEventListener("mouseover", this.onMouseHover);
element.addEventListener("mouseout", this.onMouseHoverOut);
});
document.addEventListener("mousemove", this.onMouseMove);
document.addEventListener("mousedown", this.onMouseHover);
document.addEventListener("mouseup", this.onMouseHoverOut);
document.addEventListener("mouseenter", this.onMouseEnter);
document.addEventListener("mouseleave", this.onMouseLeave);
}, 100);
},
destroy() {
console.log("destroy");
this.withHover.forEach((element) => {
element.removeEventListener("mouseover", this.onMouseHover);
element.removeEventListener("mouseout", this.onMouseHoverOut);
});
document.removeEventListener("mousemove", this.onMouseMove);
document.removeEventListener("mousedown", this.onMouseHover);
document.removeEventListener("mouseup", this.onMouseHoverOut);
document.removeEventListener("mouseenter", this.onMouseEnter);
document.removeEventListener("mouseleave", this.onMouseLeave);
},
onMouseEnter() {
this.cursorBig.style.opacity = 1;
this.cursorSmall.style.opacity = 1;
},
onMouseLeave() {
this.cursorBig.style.opacity = 0;
this.cursorSmall.style.opacity = 0;
},
onMouseMove(e) {
this.cursorSmall.style.opacity = 1;
gsap.to(this.cursorBig, 0.4, {
x: e.clientX - 18.5,
y: e.clientY - 18.5,
});
gsap.to(this.cursorSmall, 0.1, {
x: e.clientX - 4,
y: e.clientY - 4,
});
},
onMouseHover() {
gsap.to(this.cursorBig, 0.3, {
scale: 3,
});
},
onMouseHoverOut() {
gsap.to(this.cursorBig, 0.3, {
scale: 1,
});
},
},
};
</script>
<style>
#media screen and (min-width: 1100px) {
* {
cursor: none !important;
}
.custom-cursor__ball {
position: fixed;
top: 0;
left: 0;
mix-blend-mode: difference;
z-index: 99999;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease;
}
.custom-cursor__ball--big {
content: "";
width: 35px;
height: 35px;
background: black;
border-radius: 50%;
}
.custom-cursor__ball--small {
content: "";
width: 6px;
height: 6px;
background: #000;
border-radius: 50%;
}
}
</style>
i currently facing the same issue, but it solved after adding,
this.$next.tick()
so, i just attached the handler when the components is mounted :
mounted() {
this.$nextTick(() => {
this.transitionController()
window.addEventListener('scroll', () => {
this.transitionController()
})
})
},

Mix-Blend mode doesn't work on custom cursor

I would like to add a mix-blend-mode: difference to the inner cursor, but I can't get it to work. I heard that the position: fixed applied to the cursor may cause the problem here. But I can't remove that property, because the cursor needs to follow when scrolling down. Does anyone know how to fix the problem here? Thank you in advance.
class Demo {
constructor() {
this.initCursor();
this.initHovers();
}
initCursor() {
const { Back } = window;
this.outerCursor = document.querySelector(".circle-cursor-outer");
this.innerCursor = document.querySelector(".circle-cursor-inner");
this.outerCursorBox = this.outerCursor.getBoundingClientRect();
this.outerCursorSpeed = 0;
this.easing = Back.easeOut.config(1.7);
this.clientX = -100;
this.clientY = -100;
this.showCursor = false;
const unveilCursor = () => {
TweenMax.set(this.innerCursor, {
x: this.clientX,
y: this.clientY
});
TweenMax.set(this.outerCursor, {
x: this.clientX - this.outerCursorBox.width / 2,
y: this.clientY - this.outerCursorBox.height / 2
});
setTimeout(() => {
this.outerCursorSpeed = 0.2;
}, 100);
this.showCursor = true;
};
document.addEventListener("mousemove", unveilCursor);
document.addEventListener("mousemove", e => {
this.clientX = e.clientX;
this.clientY = e.clientY;
});
const render = () => {
TweenMax.set(this.innerCursor, {
rotation: 90,
x: this.clientX,
y: this.clientY
});
if (!this.isStuck) {
TweenMax.to(this.outerCursor, this.outerCursorSpeed, {
x: this.clientX - this.outerCursorBox.width / 2,
y: this.clientY - this.outerCursorBox.height / 2
});
}
if (this.showCursor) {
document.removeEventListener("mousemove", unveilCursor);
}
requestAnimationFrame(render);
};
requestAnimationFrame(render);
}
initHovers() {
const handleMouseEnter = e => {
this.isStuck = true;
const target = e.currentTarget;
const box = target.getBoundingClientRect();
this.outerCursorOriginals = {
width: this.outerCursorBox.width,
height: this.outerCursorBox.height
};
TweenMax.to(this.innerCursor, 0.2, {
x: box.left,
y: box.top,
width: box.width,
height: box.height,
opacity: 0.4,
});
};
const handleMouseLeave = () => {
this.isStuck = false;
TweenMax.to(this.innerCursor, 0.2, {
width: this.outerCursorOriginals.width,
height: this.outerCursorOriginals.height,
opacity: 0.2,
});
};
const mainNavHoverTween = TweenMax.to(this.innerCursor, 0.3, {
ease: this.easing,
paused: true,
opacity: 1,
width: 70,
height: 70,
left: -35,
top: -35
});
const mainNavMouseEnter = () => {
this.outerCursorSpeed = 0;
TweenMax.set(this.innerCursor, { opacity: 1 });
mainNavHoverTween.play();
};
const mainNavMouseLeave = () => {
this.outerCursorSpeed = 0.2;
TweenMax.set(this.innerCursor, { opacity: 1 });
mainNavHoverTween.reverse();
};
const mainNavLinks = document.querySelectorAll("a");
mainNavLinks.forEach(item => {
item.addEventListener("mouseenter", mainNavMouseEnter);
item.addEventListener("mouseleave", mainNavMouseLeave);
});
}
}
const demo = new Demo();
body{
height: 500vh;
}
a{
color: black;
}
.circle-cursor {
position: fixed;
left: 0;
top: 0;
pointer-events: none;
border-radius: 100%;
}
.circle-cursor-outer-badge {
opacity: 0.5;
transition: all 500ms ease-out;
}
.circle-cursor-inner {
width: 22.5px;
height: 22.5px;
left: -11.25px;
top: -11.25px;
z-index: 11000;
mix-blend-mode: difference;
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
I am a link
<div class="circle-cursor circle-cursor-outer">
<img class="circle-cursor-outer-badge" draggable="false" width="120" src="https://svgshare.com/i/H2V.svg">
</div>
<div class="circle-cursor circle-cursor-inner"></div>
</body>
Just set background:white to both body and .circle-cursor-inner:
class Demo {
constructor() {
this.initCursor();
this.initHovers();
}
initCursor() {
const { Back } = window;
this.outerCursor = document.querySelector(".circle-cursor-outer");
this.innerCursor = document.querySelector(".circle-cursor-inner");
this.outerCursorBox = this.outerCursor.getBoundingClientRect();
this.outerCursorSpeed = 0;
this.easing = Back.easeOut.config(1.7);
this.clientX = -100;
this.clientY = -100;
this.showCursor = false;
const unveilCursor = () => {
TweenMax.set(this.innerCursor, {
x: this.clientX,
y: this.clientY
});
TweenMax.set(this.outerCursor, {
x: this.clientX - this.outerCursorBox.width / 2,
y: this.clientY - this.outerCursorBox.height / 2
});
setTimeout(() => {
this.outerCursorSpeed = 0.2;
}, 100);
this.showCursor = true;
};
document.addEventListener("mousemove", unveilCursor);
document.addEventListener("mousemove", e => {
this.clientX = e.clientX;
this.clientY = e.clientY;
});
const render = () => {
TweenMax.set(this.innerCursor, {
rotation: 90,
x: this.clientX,
y: this.clientY
});
if (!this.isStuck) {
TweenMax.to(this.outerCursor, this.outerCursorSpeed, {
x: this.clientX - this.outerCursorBox.width / 2,
y: this.clientY - this.outerCursorBox.height / 2
});
}
if (this.showCursor) {
document.removeEventListener("mousemove", unveilCursor);
}
requestAnimationFrame(render);
};
requestAnimationFrame(render);
}
initHovers() {
const handleMouseEnter = e => {
this.isStuck = true;
const target = e.currentTarget;
const box = target.getBoundingClientRect();
this.outerCursorOriginals = {
width: this.outerCursorBox.width,
height: this.outerCursorBox.height
};
TweenMax.to(this.innerCursor, 0.2, {
x: box.left,
y: box.top,
width: box.width,
height: box.height,
opacity: 0.4,
});
};
const handleMouseLeave = () => {
this.isStuck = false;
TweenMax.to(this.innerCursor, 0.2, {
width: this.outerCursorOriginals.width,
height: this.outerCursorOriginals.height,
opacity: 0.2,
});
};
const mainNavHoverTween = TweenMax.to(this.innerCursor, 0.3, {
ease: this.easing,
paused: true,
opacity: 1,
width: 70,
height: 70,
left: -35,
top: -35
});
const mainNavMouseEnter = () => {
this.outerCursorSpeed = 0;
TweenMax.set(this.innerCursor, { opacity: 1 });
mainNavHoverTween.play();
};
const mainNavMouseLeave = () => {
this.outerCursorSpeed = 0.2;
TweenMax.set(this.innerCursor, { opacity: 1 });
mainNavHoverTween.reverse();
};
const mainNavLinks = document.querySelectorAll("a");
mainNavLinks.forEach(item => {
item.addEventListener("mouseenter", mainNavMouseEnter);
item.addEventListener("mouseleave", mainNavMouseLeave);
});
}
}
const demo = new Demo();
body{
height: 500vh;
background:white;
}
a{
color: black;
}
.circle-cursor {
position: fixed;
left: 0;
top: 0;
pointer-events: none;
border-radius: 100%;
}
.circle-cursor-outer-badge {
opacity: 0.5;
transition: all 500ms ease-out;
}
.circle-cursor-inner {
width: 22.5px;
height: 22.5px;
left: -11.25px;
top: -11.25px;
z-index: 11000;
mix-blend-mode: difference;
background: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
I am a link
<div class="circle-cursor circle-cursor-outer">
<img class="circle-cursor-outer-badge" draggable="false" width="120" src="https://svgshare.com/i/H2V.svg">
</div>
<div class="circle-cursor circle-cursor-inner"></div>
</body>

How to generate Canvas layer with on click button

i have a question - how to draw canvas layer (for example just simple square) with event on click on button in Vue.js? I have stage and on that stage with position x:0, y:0 i want after click on button to generate that square and with drag and drop to position it on that stage? I'm using Konvajs for creating Canvas
Can somebody help me?
<template>
<div id="main">
<h1></h1>
<div id="obszarroboczy" style="width: 500px; height: 600px;">
<v-stage ref="stage"
:config="configKonva"
#dragstart="handleDragstart"
#dragend="handleDragend">
<v-layer ref="layer">
<v-star
v-for="item in list"
:key="item.id"
:config="item"></v-star>
</v-layer>
<v-layer ref="dragLayer"></v-layer>
</v-stage>
</div>
<div class="col-md-6">
<button v-on:click="handleClick" id="more_canvas">More</button>
</div>
</div>
</template>
<script>
import Vue from "vue";
import axios from "axios";
import draggable from "vuedraggable";
import swal from "sweetalert2";
import VueKonva from "vue-konva";
export default {
name: "EnumCurrencyIndex",
$mount: "#main",
components: {
draggable
},
data() {
return {
model: [],
editable: true,
isDragging: false,
delayedDragging: false,
type: "currency",
editedElement: null,
newElement: "",
list: [],
configKonva: {
width: 400,
height: 400
},
configCircle: {
x: 100,
y: 100,
radius: 70,
fill: 'red',
stroke: 'black',
strokeWidth: 4
},
vm: {}
};
},
beforeMount() {
this.fetchData();
},
computed: {
dragOptions() {
return {
animation: 0,
group: "description",
disabled: !this.editable,
ghostClass: "ghost"
};
},
listString() {
return this.model;
},
dragCanvas() {
return this.model;
}
},
watch: {
$route: "fetchData",
isDragging(newValue) {
if (newValue) {
this.delayedDragging = true;
return;
}
this.$nextTick(() => {
this.delayedDragging = false;
});
}
},
methods: {
handleDragstart(starComponent) {
var vm = this;
const shape = starComponent.getStage();
const dragLayer = vm.$refs.dragLayer.getStage();
const stage = vm.$refs.stage.getStage();
// moving to another layer will improve dragging performance
shape.moveTo(dragLayer);
stage.draw();
starComponent.config.shadowOffsetX = 15;
starComponent.config.shadowOffsetY = 15;
starComponent.config.scaleX = starComponent.config.startScale * 1.2;
starComponent.config.scaleY = starComponent.config.startScale * 1.2;
},
handleDragend(starComponent) {
var vm = this;
const shape = starComponent.getStage();
const layer = vm.$refs.layer.getStage();
const stage = vm.$refs.stage.getStage();
shape.moveTo(layer);
stage.draw();
shape.to({
duration: 0.5,
easing: Konva.Easings.ElasticEaseOut,
scaleX: starComponent.config.startScale,
scaleY: starComponent.config.startScale,
shadowOffsetX: 5,
shadowOffsetY: 5
});
},
handleClick(configCircle) {
var vm = this;
const shape = vm.$refs.layer.getStage();
const layer = vm.$refs.layer.getStage();
const stage = vm.$refs.stage.getStage();
console.log(1);
layer.add(configCircle);
stage.add(layer);
},
haveIntersection(r1, r2) {
return !(
r2.x > r1.x + r1.width ||
r2.x + r2.width < r1.x ||
r2.y > r1.y + r1.height ||
r2.y + r2.height < r1.y
);
},
orderList() {
this.model = this.model.sort((one, two) => {
return one.position - two.position;
});
},
onMove({ relatedContext, draggedContext }) {
const relatedElement = relatedContext.element;
const draggedElement = draggedContext.element;
return (
(!relatedElement || !relatedElement.fixed) && !draggedElement.fixed
);
},
fetchData() {
var vm = this;
axios
.get(`/api/${this.resource}?type=${this.type}`)
.then(function(response) {
Vue.set(vm.$data, "model", response.data.model);
})
.catch(function(error) {
console.log(error);
});
}
},
mounted() {
var box = document.getElementById("obszarroboczy");
this.configKonva.width = box.offsetWidth;
this.configKonva.height = box.offsetHeight;
var vm = this;
for (let n = 0; n < 30; n++) {
const scale = Math.random();
const stage = vm.$refs.stage.getStage();
vm.list.push({
x: Math.random() * stage.getWidth(),
y: Math.random() * stage.getHeight(),
rotation: Math.random() * 180,
numPoints: 5,
innerRadius: 30,
outerRadius: 50,
fill: "#89b717",
opacity: 0.8,
draggable: true,
scaleX: scale,
scaleY: scale,
shadowColor: "black",
shadowBlur: 10,
shadowOffsetX: 5,
shadowOffsetY: 5,
shadowOpacity: 0.6,
startScale: scale
});
};
},
directives: {
"element-focus": function(el, binding) {
if (binding.value) {
el.focus();
}
}
}
};
</script>
<style>
#obszarroboczy {
width: 100px;
height: 300px;
}
.normal {
background-color: grey;
}
.table td {
width: 100px;
height: 100px;
background: white;
border: 2px dotted black;
max-width: 100px;
padding: 5px;
}
.drag {
display: flex;
flex-direction: row;
}
.list {
flex-grow: 1;
max-width: 47%;
margin-right: 40px;
}
.name {
width: 50%;
display: inline-block;
height: 50px;
background: pink;
border: 5px green solid;
box-sizing: border-box;
padding: 5px;
}
.name.large {
width: 100%;
}
.dragArea {
min-height: 100px;
}
.dragArea img {
margin: 3px;
cursor: pointer;
}
</style>
var mainCanvas = new Vue({
el: '#main', // the element where the method wil lrender the canvas to
data: {
name: 'Vue.js'
},
methods: {
handleClick: function (event) { // handleClick is the method name for the button
var stage = new Konva.Stage({ // this line till the stage.add() line renders the draggable square
container: 'obszarroboczy',
width: 500,
height: 500
});
var layer = new Konva.Layer();
var rect = new Konva.Rect({
x: 0,
y: 0,
width: 100,
height: 100,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
draggable: true
});
layer.add(rect);
stage.add(layer);
}
}
});
I added comments to explain what certain important lines does but you can check out the official KonvaJS Docs in GitHub for a more detailed explanation on what each line above does.

Complex SVG animation in ReactJS

I am using React to build an animated intro that comprises three taglines paired with animated SVG icons.
I opted for TweenMax to manage the SVG animations, due to the solid cross-browser support it offers. This also would allow me to perform a simple morph of the d attribute on <path> elements. I am combining the aforementioned with ReactTransitionGroup
However, I ran into the following problems while trying to make TweenMax and React play nice:
While opting for component state, componentWillReceiveProps is somehow called twice in console. This would mean, my animation method would be called twice. What is causing this and might storing the tag index using Redux be a better option in this use case?
The way that I am animating currently seems very crude, e.g. repeating this.refs and findDOMNode() repeatedly. Surely there must be a better way? In which ways could the structure of my code be improved?
I am completely stumped and would love to be pointed in the right direction.
Kind regards, Jason
Code
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background: rgb(240, 90, 48);
font: bold 1em sans-serif;
color: rgb(255, 255, 255);
text-align: center;
}
.reveal-wrap {
position: absolute;
width: 200px;
height: 200px;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
overflow: hidden;
}
.reveal-icon {
width: 100px;
height: 100px;
margin: 0 auto 2em auto;
}
.reveal-icon svg {
width: 100%;
height: 100%;
}
.reveal-icon .fill {
fill: rgb(251, 163, 10);
}
.reveal-icon .mask {
fill: rgb(240, 90, 48);
}
.reveal-icon .stroke {
stroke: rgb(251, 163, 10);
stroke-linecap: round;
stroke-width: 5;
}
.reveal-text {
position: absolute;
width: 100%;
}
.switch-locale {
position: fixed;
top: 1em;
left: 1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.25/browser.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<script src="https://fb.me/react-with-addons-0.13.3.min.js"></script>
<div id="react-root"></div>
<script type="text/babel">
const ReactTransitionGroup = React.addons.TransitionGroup
const UI_TEXT = {
EN_US: {
REVEAL: [
{ ID: 0, TEXT: 'Tagline 1' },
{ ID: 1, TEXT: 'Tagline 2' },
{ ID: 2, TEXT: 'Tagline 3' }
]
},
NL_NL: {
REVEAL: [
{ ID: 0, TEXT: 'Slagzin 1' },
{ ID: 1, TEXT: 'Slagzin 2' },
{ ID: 2, TEXT: 'Slagzin 3' }
]
}
}
class Reveal extends React.Component {
constructor() {
super();
this.state = {
index: 0,
locale: 'EN_US'
}
}
nextTagline() {
this.setState({index: this.state.index + 1})
console.log('this.state.index # ' + this.state.index)
}
switchLocale() {
let locale = (this.state.locale === 'EN_US') ? 'NL_NL' : 'EN_US'
this.setState({locale})
}
render() {
return (
<ReactTransitionGroup className='reveal-wrap' component='div'>
<RevealIcon tag={this.state.index} nextTag={() => this.nextTagline()} />
<RevealText tag={this.state.index} locale={this.state.locale} key={this.state.index} />
<SwitchLocale switchLocale={() => this.switchLocale()} />
</ReactTransitionGroup>
)
}
}
class RevealText extends React.Component {
fadeIn(callback, delay) {
TweenLite.fromTo(React.findDOMNode(this), 0.5,
{
y: '100px',
opacity: 0
},
{
y: 0,
delay: delay,
opacity: 1,
ease: Quad.easeOut,
onComplete: callback,
onCompleteScope: this
}
)
}
fadeOut(callback, delay) {
TweenLite.fromTo(React.findDOMNode(this), 0.5,
{
y: 0,
opacity: 1
},
{
y: '+=100px',
delay: delay,
opacity: 0,
ease: Quad.easeIn,
onComplete: callback,
onCompleteScope: this
}
)
}
componentWillAppear(callback) {
//console.log('RevealText will appear')
this.fadeIn(callback, 1)
}
componentDidAppear() {
//console.log("RevealText did appear")
}
componentWillLeave(callback) {
this.fadeOut(callback, 0)
}
componentDidLeave() {
//console.log('RevealText did leave')
}
componentWillEnter(callback) {
this.fadeIn(callback, 1)
}
componentDidEnter() {
//console.log("RevealText did enter")
}
render() {
return (
<div className='reveal-text'>
{ UI_TEXT[this.props.locale].REVEAL[this.props.tag].TEXT }
</div>
)
}
}
class RevealIcon extends React.Component {
componentWillAppear(callback) {
const HAND_1 = [React.findDOMNode(this.refs.HAND_1),
React.findDOMNode(this.refs.HAND_1_MASK),
React.findDOMNode(this.refs.HAND_1_THUMB)]
const HAND_2 = [React.findDOMNode(this.refs.HAND_2),
React.findDOMNode(this.refs.HAND_2_MASK)]
const HAND_3 = React.findDOMNode(this.refs.HAND_LINES)
const HAND_4 = [React.findDOMNode(this.refs.HAND_LINES_1),
React.findDOMNode(this.refs.HAND_LINES_2),
React.findDOMNode(this.refs.HAND_LINES_3),
React.findDOMNode(this.refs.HAND_LINES_4),
React.findDOMNode(this.refs.HAND_LINES_5),
React.findDOMNode(this.refs.HAND_LINES_6),
React.findDOMNode(this.refs.HAND_LINES_7),
React.findDOMNode(this.refs.HAND_LINES_8)]
let anim = new TimelineMax({
delay: 2,
onComplete: this.props.nextTag,
onCompleteScope: this
})
anim.fromTo(HAND_1, 0.5,
{
y: '-=100px',
x: '+=100px',
opacity: 0
},
{
y: 0,
x: 0,
opacity: 1,
ease: Quad.easeOut
})
.fromTo(HAND_2, 0.5,
{
y: '-=100px',
x: '-=100px',
opacity: 0
},
{
y: 0,
x: 0,
opacity: 1,
ease: Quad.easeOut
}, '-=0.20')
.fromTo(HAND_3, 0.75,
{
scaleX: 0.5,
scaleY: 0.5,
transformOrigin: '50% 50%'
},
{
scaleX: 1,
scaleY: 1,
ease: Quad.easeOut
})
.fromTo(HAND_4, 0.5,
{
opacity: 0,
},
{
opacity: 1,
ease: Quad.easeOut
}, '-=0.75')
.fromTo(HAND_4, 1,
{
'stroke-dasharray': '25px',
'stroke-dashoffset': '0px'
},
{
'stroke-dasharray': '25px',
'stroke-dashoffset': '25px',
ease: Power3.easeOut
}, '-=0.75')
.set({}, {}, '+=1')
// .set is used to lengthen the animation by 1 second
}
componentWillReceiveProps(nextProps) {
console.log('RevealIcon will receive props', 'nextProps.tag: ' + nextProps.tag)
if(nextProps.tag === 1){
// Animation code / reference to method here
} else if (nextProps.tag === 2) {
// Animation code / reference to method here
} else if (nextProps.tag === 3) {
// Animation code / reference to method here
}
}
render() {
return (
<div className='reveal-icon' >
<svg height="200" width="200" viewBox="0, 0, 200, 200">
<path ref="HAND_1" className="fill" d="M146.8,79.9l-55.2,55.2c-1.8,1.8-4.8,1.8-6.7,0c-1.8-1.8-1.8-4.8,0-6.7h0l18.4-18.4l-3.3-3.3
l-18.4,18.4c-0.9,0.9-2.1,1.4-3.3,1.4s-2.5-0.5-3.3-1.4c-0.9-0.9-1.4-2.1-1.4-3.3c0-1.3,0.5-2.5,1.4-3.3L93.3,100L90,96.7
l-18.4,18.4c-1.8,1.8-4.8,1.8-6.7,0c-1.8-1.8-1.8-4.8,0-6.7l41.8-41.8l-3.3-3.3L61.5,105c-3.7,3.7-3.7,9.7,0,13.4
c1.8,1.8,4.3,2.8,6.7,2.8c0.2,0,0.4,0,0.6,0c0,0.2,0,0.4,0,0.6c0,2.5,1,4.9,2.8,6.7c1.8,1.8,4.2,2.8,6.7,2.8c0.2,0,0.4,0,0.6,0
c-0.2,2.6,0.7,5.3,2.7,7.3c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l55.2-55.2L146.8,79.9z"/>
<path ref="HAND_2_MASK" className="mask" d="M138.5,105l-22.7-22.7L83.3,49.8L49.8,83.3l32.5,32.5l22.7,22.7
c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8c2-2,2.9-4.7,2.7-7.3c0.2,0,0.4,0,0.6,0c2.5,0,4.9-1,6.7-2.8
c1.8-1.8,2.8-4.2,2.8-6.7c0-0.2,0-0.4,0-0.6c0.2,0,0.4,0,0.6,0c2.4,0,4.8-0.9,6.7-2.8c1.8-1.8,2.8-4.2,2.8-6.7
C141.2,109.2,140.2,106.8,138.5,105z"/>
<path ref="HAND_2" className="fill" d="M138.5,105L83.3,49.8l-3.3,3.3l55.2,55.2c0.9,0.9,1.4,2.1,1.4,3.3c0,1.3-0.5,2.5-1.4,3.3
c-1.8,1.8-4.8,1.8-6.7,0L110,96.7l-3.3,3.3l18.4,18.4c0.9,0.9,1.4,2.1,1.4,3.3c0,1.3-0.5,2.5-1.4,3.3c-0.9,0.9-2.1,1.4-3.3,1.4
s-2.5-0.5-3.3-1.4L100,106.7l-3.3,3.3l18.4,18.4c1.8,1.8,1.8,4.8,0,6.7c-1.8,1.8-4.8,1.8-6.7,0L53.2,79.9l-3.3,3.3l55.2,55.2
c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8c2-2,2.9-4.7,2.7-7.3c0.2,0,0.4,0,0.6,0c2.5,0,4.9-1,6.7-2.8
c1.8-1.8,2.8-4.2,2.8-6.7c0-0.2,0-0.4,0-0.6c0.2,0,0.4,0,0.6,0c2.4,0,4.8-0.9,6.7-2.8c1.8-1.8,2.8-4.2,2.8-6.7
C141.2,109.2,140.2,106.8,138.5,105z"/>
<path ref="HAND_1_MASK" className="mask" d="M116.7,49.8l-5,5l-3.3-3.3c-1.8-1.8-4.2-2.8-6.7-2.8c-2.5,0-4.9,1-6.7,2.8
L73.2,73.2c-3.7,3.7-3.7,9.7,0,13.4c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l20.1-20.1l5-5l8.4-8.4L116.7,49.8z"/>
<path ref="HAND_1_THUMB" className="fill" d="M116.7,49.8l-5,5l-3.3-3.3l0,0c-1.8-1.8-4.2-2.8-6.7-2.8c-2.5,0-4.9,1-6.7,2.8
L73.2,73.2c-3.7,3.7-3.7,9.7,0,13.4c1.8,1.8,4.3,2.8,6.7,2.8c2.4,0,4.8-0.9,6.7-2.8l20.1-20.1l-3.3-3.3L83.3,83.3
c-1.8,1.8-4.8,1.8-6.7,0s-1.8-4.8,0-6.7l21.7-21.7c0.9-0.9,2.1-1.4,3.3-1.4c1.3,0,2.5,0.5,3.3,1.4l6.7,6.7l8.4-8.4L116.7,49.8z"/>
<g ref="HAND_LINES">
<line ref="HAND_LINES_8" className="stroke" x1="32.6" y1="32.6" x2="49.4" y2="49.4"/>
<line ref="HAND_LINES_7" className="stroke" x1="4.7" y1="100" x2="28.4" y2="100"/>
<line ref="HAND_LINES_6" className="stroke" x1="32.6" y1="167.4" x2="49.4" y2="150.6"/>
<line ref="HAND_LINES_5" className="stroke" x1="100" y1="195.3" x2="100" y2="171.6"/>
<line ref="HAND_LINES_4" className="stroke" x1="167.4" y1="167.4" x2="150.6" y2="150.6"/>
<line ref="HAND_LINES_3" className="stroke" x1="195.3" y1="100" x2="171.6" y2="100"/>
<line ref="HAND_LINES_2" className="stroke" x1="167.4" y1="32.6" x2="150.6" y2="49.4"/>
<line ref="HAND_LINES_1" className="stroke" x1="100" y1="4.7" x2="100" y2="28.4"/>
</g>
</svg>
</div>
)
}
}
class SwitchLocale extends React.Component {
render() {
return (
<button className='switch-locale' onClick={this.props.switchLocale}>
Switch locale
</button>
)
}
}
React.render(<Reveal/>, document.getElementById('react-root'))
</script>
On the left, the animation I currently have created in React, on the right, the icons I still have to implement in the same manner.

modal class not working on buttons

I have this form.
<form action="../massmail.php" method="post">
<label>Periode:</label>
<div class="form-inline"><input class="form-inline input-small" name="maand" type="text" placeholder="bijv.: juni" />
<input class="input-mini" name="jaar" type="text" value="2015" />
</div>
<br /> <label>Inleidende tekst:</label>
<div>
<textarea name="inleiding" rows="5"></textarea>
</div>
<br /> <label>url van de nieuwsbrief:</label>
<div>
<input class="input-xxlarge" name="link" type="url" value="" />
</div>
<div class="form-actions">
<button class="btn btn-primary" type="submit">Verzenden</button>
<button class="btn" formaction="../voorbeeld.php" formmethod="get" type="submit">Voorbeeld</button>
<button class="btn" type="button">Annuleren</button> </div>
</form>
In the last <div> are the form controlls. The second button is what I want to change. It submits the form, overriding the default form action and gives a preview of what would be sent if the actual submit button (Verzenden) is pressed.
This all works, but I want to give this preview in a modal window (pop up). For some reason the modal class only works for <a> elements. I have previously solved this by setting the <a> element class to btn modal, but in this case I need the formaction so I need an actual button.
Things I have tried and results.
<button class="btn modal" formaction="../voorbeeld.php" formmethod="get" type="submit" rel="{handler: 'iframe', size: {x: 920, y: 530}}">Voorbeeld</button>
Result: Form get's handled the way I expect, but no popup, results just get displayed as if a link is opened to the php page.
<a class="modal" href="voorbeeld.php" rel="{handler: 'iframe', size: {x: 920, y: 530}}"> <button class="btn" type="button" formaction="../voorbeeld.php" formmethod="get" type="submit">Voorbeeld MOD</button> </a>
Result: This opens the popup with the php page, but the results from the form don't get through to the php page.
Included as snippet is the modal.css, which doesn't seem to have anything <a> or <button> specific. modal.js is the javascript, but I'm not really familiar with that.
I'm doing all of this in a custom html module in Joomla! 3.4.3.
Any help would be appreciated
/*
MIT-style license
#author Harald Kirschner <mail [at] digitarald.de>
#author Rouven Weßling <me [at] rouvenwessling.de>
#copyright Author
*/
var SqueezeBox = {
presets: {
onOpen: function() {},
onClose: function() {},
onUpdate: function() {},
onResize: function() {},
onMove: function() {},
onShow: function() {},
onHide: function() {},
size: {
x: 600,
y: 450
},
sizeLoading: {
x: 200,
y: 150
},
marginInner: {
x: 20,
y: 20
},
marginImage: {
x: 50,
y: 75
},
handler: false,
target: null,
closable: true,
closeBtn: true,
zIndex: 65555,
overlayOpacity: .7,
classWindow: "",
classOverlay: "",
overlayFx: {},
resizeFx: {},
contentFx: {},
parse: false,
parseSecure: false,
shadow: true,
overlay: true,
document: null,
ajaxOptions: {}
},
initialize: function(e) {
if (this.options) return this;
this.presets = Object.merge(this.presets, e);
this.doc = this.presets.document || document;
this.options = {};
this.setOptions(this.presets).build();
this.bound = {
window: this.reposition.bind(this, [null]),
scroll: this.checkTarget.bind(this),
close: this.close.bind(this),
key: this.onKey.bind(this)
};
this.isOpen = this.isLoading = false;
return this
},
build: function() {
this.overlay = new Element("div", {
id: "sbox-overlay",
"aria-hidden": "true",
styles: {
zIndex: this.options.zIndex
},
tabindex: -1
});
this.win = new Element("div", {
id: "sbox-window",
role: "dialog",
"aria-hidden": "true",
styles: {
zIndex: this.options.zIndex + 2
}
});
if (this.options.shadow) {
if (Browser.chrome || Browser.safari && Browser.version >= 3 || Browser.opera && Browser.version >= 10.5 || Browser.firefox && Browser.version >= 3.5 || Browser.ie && Browser.version >= 9) {
this.win.addClass("shadow")
} else if (!Browser.ie6) {
var e = (new Element("div", {
"class": "sbox-bg-wrap"
})).inject(this.win);
var t = function(e) {
this.overlay.fireEvent("click", [e])
}.bind(this);
["n", "ne", "e", "se", "s", "sw", "w", "nw"].each(function(n) {
(new Element("div", {
"class": "sbox-bg sbox-bg-" + n
})).inject(e).addEvent("click", t)
})
}
}
this.content = (new Element("div", {
id: "sbox-content"
})).inject(this.win);
this.closeBtn = (new Element("a", {
id: "sbox-btn-close",
href: "#",
role: "button"
})).inject(this.win);
this.closeBtn.setProperty("aria-controls", "sbox-window");
this.fx = {
overlay: (new Fx.Tween(this.overlay, Object.merge({
property: "opacity",
onStart: Events.prototype.clearChain,
duration: 250,
link: "cancel"
}, this.options.overlayFx))).set(0),
win: new Fx.Morph(this.win, Object.merge({
onStart: Events.prototype.clearChain,
unit: "px",
duration: 750,
transition: Fx.Transitions.Quint.easeOut,
link: "cancel",
unit: "px"
}, this.options.resizeFx)),
content: (new Fx.Tween(this.content, Object.merge({
property: "opacity",
duration: 250,
link: "cancel"
}, this.options.contentFx))).set(0)
};
document.id(this.doc.body).adopt(this.overlay, this.win)
},
assign: function(e, t) {
return (document.id(e) || $$(e)).addEvent("click", function() {
return !SqueezeBox.fromElement(this, t)
})
},
open: function(e, t) {
this.initialize();
if (this.element != null) this.trash();
this.element = document.id(e) || false;
this.setOptions(Object.merge(this.presets, t || {}));
if (this.element && this.options.parse) {
var n = this.element.getProperty(this.options.parse);
if (n && (n = JSON.decode(n, this.options.parseSecure))) this.setOptions(n)
}
this.url = (this.element ? this.element.get("href") : e) || this.options.url || "";
this.assignOptions();
var r = r || this.options.handler;
if (r) return this.setContent(r, this.parsers[r].call(this, true));
var i = false;
return this.parsers.some(function(e, t) {
var n = e.call(this);
if (n) {
i = this.setContent(t, n);
return true
}
return false
}, this)
},
fromElement: function(e, t) {
return this.open(e, t)
},
assignOptions: function() {
this.overlay.addClass(this.options.classOverlay);
this.win.addClass(this.options.classWindow)
},
close: function(e) {
var t = typeOf(e) == "domevent";
if (t) e.stop();
if (!this.isOpen || t && !Function.from(this.options.closable).call(this, e)) return this;
this.fx.overlay.start(0).chain(this.toggleOverlay.bind(this));
this.win.setProperty("aria-hidden", "true");
this.fireEvent("onClose", [this.content]);
this.trash();
this.toggleListeners();
this.isOpen = false;
return this
},
trash: function() {
this.element = this.asset = null;
this.content.empty();
this.options = {};
this.removeEvents().setOptions(this.presets).callChain()
},
onError: function() {
this.asset = null;
this.setContent("string", this.options.errorMsg || "An error occurred")
},
setContent: function(e, t) {
if (!this.handlers[e]) return false;
this.content.className = "sbox-content-" + e;
this.applyTimer = this.applyContent.delay(this.fx.overlay.options.duration, this, this.handlers[e].call(this, t));
if (this.overlay.retrieve("opacity")) return this;
this.toggleOverlay(true);
this.fx.overlay.start(this.options.overlayOpacity);
return this.reposition()
},
applyContent: function(e, t) {
if (!this.isOpen && !this.applyTimer) return;
this.applyTimer = clearTimeout(this.applyTimer);
this.hideContent();
if (!e) {
this.toggleLoading(true)
} else {
if (this.isLoading) this.toggleLoading(false);
this.fireEvent("onUpdate", [this.content], 20)
} if (e) {
if (["string", "array"].contains(typeOf(e))) {
this.content.set("html", e)
} else {
this.content.adopt(e)
}
}
this.callChain();
if (!this.isOpen) {
this.toggleListeners(true);
this.resize(t, true);
this.isOpen = true;
this.win.setProperty("aria-hidden", "false");
this.fireEvent("onOpen", [this.content])
} else {
this.resize(t)
}
},
resize: function(e, t) {
this.showTimer = clearTimeout(this.showTimer || null);
var n = this.doc.getSize(),
r = this.doc.getScroll();
this.size = Object.merge(this.isLoading ? this.options.sizeLoading : this.options.size, e);
var i = self.getSize();
if (this.size.x == i.x) {
this.size.y = this.size.y - 50;
this.size.x = this.size.x - 20
}
if (n.x > 979) {
var s = {
width: this.size.x,
height: this.size.y,
left: (r.x + (n.x - this.size.x - this.options.marginInner.x) / 2).toInt(),
top: (r.y + (n.y - this.size.y - this.options.marginInner.y) / 2).toInt()
}
} else {
var s = {
width: n.x - 40,
height: n.y,
left: (r.x + 10).toInt(),
top: (r.y + 20).toInt()
}
}
this.hideContent();
if (!t) {
this.fx.win.start(s).chain(this.showContent.bind(this))
} else {
this.win.setStyles(s);
this.showTimer = this.showContent.delay(50, this)
}
return this.reposition()
},
toggleListeners: function(e) {
var t = e ? "addEvent" : "removeEvent";
this.closeBtn[t]("click", this.bound.close);
this.overlay[t]("click", this.bound.close);
this.doc[t]("keydown", this.bound.key)[t]("mousewheel", this.bound.scroll);
this.doc.getWindow()[t]("resize", this.bound.window)[t]("scroll", this.bound.window)
},
toggleLoading: function(e) {
this.isLoading = e;
this.win[e ? "addClass" : "removeClass"]("sbox-loading");
if (e) {
this.win.setProperty("aria-busy", e);
this.fireEvent("onLoading", [this.win])
}
},
toggleOverlay: function(e) {
if (this.options.overlay) {
var t = this.doc.getSize().x;
this.overlay.set("aria-hidden", e ? "false" : "true");
this.doc.body[e ? "addClass" : "removeClass"]("body-overlayed");
if (e) {
this.scrollOffset = this.doc.getWindow().getSize().x - t
} else {
this.doc.body.setStyle("margin-right", "")
}
}
},
showContent: function() {
if (this.content.get("opacity")) this.fireEvent("onShow", [this.win]);
this.fx.content.start(1)
},
hideContent: function() {
if (!this.content.get("opacity")) this.fireEvent("onHide", [this.win]);
this.fx.content.cancel().set(0)
},
onKey: function(e) {
switch (e.key) {
case "esc":
this.close(e);
case "up":
case "down":
return false
}
},
checkTarget: function(e) {
return e.target !== this.content && this.content.contains(e.target)
},
reposition: function() {
var e = this.doc.getSize(),
t = this.doc.getScroll(),
n = this.doc.getScrollSize();
var r = this.overlay.getStyles("height");
var i = parseInt(r.height);
if (n.y > i && e.y >= i) {
this.overlay.setStyles({
width: n.x + "px",
height: n.y + "px"
});
this.win.setStyles({
left: (t.x + (e.x - this.win.offsetWidth) / 2 - this.scrollOffset).toInt() + "px",
top: (t.y + (e.y - this.win.offsetHeight) / 2).toInt() + "px"
})
}
return this.fireEvent("onMove", [this.overlay, this.win])
},
removeEvents: function(e) {
if (!this.$events) return this;
if (!e) this.$events = null;
else if (this.$events[e]) this.$events[e] = null;
return this
},
extend: function(e) {
return Object.append(this, e)
},
handlers: new Hash,
parsers: new Hash
};
SqueezeBox.extend(new Events(function() {})).extend(new Options(function() {})).extend(new Chain(function() {}));
SqueezeBox.parsers.extend({
image: function(e) {
return e || /\.(?:jpg|png|gif)$/i.test(this.url) ? this.url : false
},
clone: function(e) {
if (document.id(this.options.target)) return document.id(this.options.target);
if (this.element && !this.element.parentNode) return this.element;
var t = this.url.match(/#([\w-]+)$/);
return t ? document.id(t[1]) : e ? this.element : false
},
ajax: function(e) {
return e || this.url && !/^(?:javascript|#)/i.test(this.url) ? this.url : false
},
iframe: function(e) {
return e || this.url ? this.url : false
},
string: function(e) {
return true
}
});
SqueezeBox.handlers.extend({
image: function(e) {
var t, n = new Image;
this.asset = null;
n.onload = n.onabort = n.onerror = function() {
n.onload = n.onabort = n.onerror = null;
if (!n.width) {
this.onError.delay(10, this);
return
}
var e = this.doc.getSize();
e.x -= this.options.marginImage.x;
e.y -= this.options.marginImage.y;
t = {
x: n.width,
y: n.height
};
for (var r = 2; r--;) {
if (t.x > e.x) {
t.y *= e.x / t.x;
t.x = e.x
} else if (t.y > e.y) {
t.x *= e.y / t.y;
t.y = e.y
}
}
t.x = t.x.toInt();
t.y = t.y.toInt();
this.asset = document.id(n);
n = null;
this.asset.width = t.x;
this.asset.height = t.y;
this.applyContent(this.asset, t)
}.bind(this);
n.src = e;
if (n && n.onload && n.complete) n.onload();
return this.asset ? [this.asset, t] : null
},
clone: function(e) {
if (e) return e.clone();
return this.onError()
},
adopt: function(e) {
if (e) return e;
return this.onError()
},
ajax: function(e) {
var t = this.options.ajaxOptions || {};
this.asset = (new Request.HTML(Object.merge({
method: "get",
evalScripts: false
}, this.options.ajaxOptions))).addEvents({
onSuccess: function(e) {
this.applyContent(e);
if (t.evalScripts !== null && !t.evalScripts) Browser.exec(this.asset.response.javascript);
this.fireEvent("onAjax", [e, this.asset]);
this.asset = null
}.bind(this),
onFailure: this.onError.bind(this)
});
this.asset.send.delay(10, this.asset, [{
url: e
}])
},
iframe: function(e) {
var t = this.doc.getSize();
if (t.x > 979) {
var n = this.options.size.x;
var r = this.options.size.y
} else {
var n = t.x;
var r = t.y - 50
}
this.asset = new Element("iframe", Object.merge({
src: e,
frameBorder: 0,
width: n,
height: r
}, this.options.iframeOptions));
if (this.options.iframePreload) {
this.asset.addEvent("load", function() {
this.applyContent(this.asset.setStyle("display", ""))
}.bind(this));
this.asset.setStyle("display", "none").inject(this.content);
return false
}
return this.asset
},
string: function(e) {
return e
}
});
SqueezeBox.handlers.url = SqueezeBox.handlers.ajax;
SqueezeBox.parsers.url = SqueezeBox.parsers.ajax;
SqueezeBox.parsers.adopt = SqueezeBox.parsers.clone;
/**
* SqueezeBox - Expandable Lightbox
*
* Allows to open various content as modal,
* centered and animated box.
*
* #version 1.3
*
* #license MIT-style license
* #author Harald Kirschner <mail [at] digitarald.de>
* #author Rouven Weßling <me [at] rouvenwessling.de>
* #copyright Author
*/
#sbox-overlay {
position: absolute;
background-color: #000;
left: 0px;
top: 0px;
}
#sbox-window {
position: absolute;
background-color: #fff;
text-align: left;
overflow: visible;
padding: 10px;
/* invalid values, but looks smoother! */
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
}
#sbox-window[aria-hidden=true],
#sbox-overlay[aria-hidden=true] {
display: none;
}
#sbox-btn-close {
position: absolute;
width: 30px;
height: 30px;
right: -15px;
top: -15px;
background: url(../images/modal/closebox.png) no-repeat center;
border: none;
}
.sbox-loading #sbox-content {
background-image: url(../images/modal/spinner.gif);
background-repeat: no-repeat;
background-position: center;
}
#sbox-content {
clear: both;
overflow: auto;
background-color: #fff;
height: 100%;
width: 100%;
}
.sbox-content-image#sbox-content {
overflow: visible;
}
#sbox-image {
display: block;
}
.sbox-content-image img {
display: block;
width: 100%;
height: 100%;
}
.sbox-content-iframe#sbox-content {
overflow: visible;
}
/* Hides scrollbars */
.body-overlayed {
overflow: hidden;
}
/* Hides flash (Firefox problem) and selects (IE) */
.body-overlayed embed,
.body-overlayed object,
.body-overlayed select {
visibility: hidden;
}
#sbox-window embed,
#sbox-window object,
#sbox-window select {
visibility: visible;
}
/* Shadows */
#sbox-window.shadow {
-webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.7);
-moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.7);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.7);
}
.sbox-bg {
position: absolute;
width: 33px;
height: 40px;
}
.sbox-bg-n {
left: 0;
top: -40px;
width: 100%;
background: url(../images/modal/bg_n.png) repeat-x;
}
.sbox-bg-ne {
right: -33px;
top: -40px;
background: url(../images/modal/bg_ne.png) no-repeat;
}
.sbox-bg-e {
right: -33px;
top: 0;
height: 100%;
background: url(../images/modal/bg_e.png) repeat-y;
}
.sbox-bg-se {
right: -33px;
bottom: -40px;
background: url(../images/modal/bg_se.png) no-repeat;
}
.sbox-bg-s {
left: 0;
bottom: -40px;
width: 100%;
background: url(../images/modal/bg_s.png) repeat-x;
}
.sbox-bg-sw {
left: -33px;
bottom: -40px;
background: url(../images/modal/bg_sw.png) no-repeat;
}
.sbox-bg-w {
left: -33px;
top: 0;
height: 100%;
background: url(../images/modal/bg_w.png) repeat-y;
}
.sbox-bg-nw {
left: -33px;
top: -40px;
background: url(../images/modal/bg_nw.png) no-repeat;
}
#-moz-document url-prefix() {
.body-overlayed {
overflow: visible;
}
}
#media (max-width: 979px) {
#sbox-window {
overflow: none;
}
#sbox-btn-close {
right: -10px;
top: -10px;
}
}
#media (max-device-width: 979px) {
#sbox-content {
-webkit-overflow-scrolling: touch;
}
#sbox-content.sbox-content-iframe {
overflow: scroll;
-webkit-overflow-scrolling: touch;
}
}
<form action="../massmail.php" method="post">
<label>Periode:</label>
<div class="form-inline">
<input class="form-inline input-small" name="maand" type="text" placeholder="bijv.: juni" />
<input class="input-mini" name="jaar" type="text" value="2015" />
</div>
<br />
<label>Inleidende tekst:</label>
<div>
<textarea name="inleiding" rows="5"></textarea>
</div>
<br />
<label>url van de nieuwsbrief:</label>
<div>
<input class="input-xxlarge" name="link" type="url" value="" />
</div>
<div class="form-actions">
<button class="btn btn-primary" type="submit">Verzenden</button>
<button class="btn" formaction="../voorbeeld.php" formmethod="get" type="submit">Voorbeeld</button>
<a href="index.php">
<button class="btn" type="button">Annuleren</button>
</a>
</div>
</form>
Problem is now solved.
I have used the second line (working popup).
<a class="btn modal" href="voorbeeld.php" rel="{handler: 'iframe', size: {x: 920, y: 530}}">Voorbeeld</a>
As mentioned this does open the popup, but it doesn't seem to process the form, so I deleted the button inside the <a> which was supposed to process the form but didn't send it to the popup.
I have then fetched the form data with Javascript by using:
var id = window.parent.document.getElementById('id').value;
For this to work, all the form elements you want to use in the popup have to have an id.
Hope this helps anyone stumbling upon this.

Categories

Resources