Working on cards.
This commit is contained in:
parent
0e8c62eb4b
commit
c3477244b9
415
dist/iwmlib.js
vendored
415
dist/iwmlib.js
vendored
@ -7548,11 +7548,6 @@
|
|||||||
|
|
||||||
handleClicks() {
|
handleClicks() {
|
||||||
this.domNode.addEventListener('click', event => {
|
this.domNode.addEventListener('click', event => {
|
||||||
console.log('handleClicks', event.isTrusted);
|
|
||||||
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
|
||||||
Therefore we make an exception and let the original click event through.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
Events.stop(event);
|
Events.stop(event);
|
||||||
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
||||||
@ -7565,10 +7560,6 @@
|
|||||||
|
|
||||||
handleClicksAsTaps() {
|
handleClicksAsTaps() {
|
||||||
this.domNode.addEventListener('click', event => {
|
this.domNode.addEventListener('click', event => {
|
||||||
console.log('handleClicksAsTaps', event.isTrusted);
|
|
||||||
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
|
||||||
Therefore we make an exception and let the original click event through.
|
|
||||||
*/
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
Events.stop(event);
|
Events.stop(event);
|
||||||
|
|
||||||
@ -7593,9 +7584,7 @@
|
|||||||
if (this.tapNodes.has(node))
|
if (this.tapNodes.has(node))
|
||||||
return true
|
return true
|
||||||
for (let [selector, handler] of this.tapHandler.entries()) {
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
console.log("selector", this.domNode.querySelectorAll(selector));
|
|
||||||
for (let obj of this.domNode.querySelectorAll(selector)) {
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
console.log("selector2", node, obj);
|
|
||||||
if (node == obj) {
|
if (node == obj) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -7658,7 +7647,6 @@
|
|||||||
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
||||||
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
||||||
Therefore we use the original click event (see constructor). */
|
Therefore we use the original click event (see constructor). */
|
||||||
console.log("simulateClick", node, node.ownerSVGElement);
|
|
||||||
if (this.isSVGNode(node)) {
|
if (this.isSVGNode(node)) {
|
||||||
if (this.triggerSVGClicks) {
|
if (this.triggerSVGClicks) {
|
||||||
let click = new Event('click');
|
let click = new Event('click');
|
||||||
@ -7691,7 +7679,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
tap(event, calledBy='unknown') {
|
tap(event, calledBy='unknown') {
|
||||||
console.log("tap", calledBy, event.alreadyTapped, event);
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
let node = this.nearestActive(event);
|
let node = this.nearestActive(event);
|
||||||
this.nodeTapped(node, event);
|
this.nodeTapped(node, event);
|
||||||
@ -7715,7 +7702,406 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
window.CardWrapper = CardWrapper;
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
/* global TweenLite */
|
||||||
|
|
||||||
|
let _HighlightEnabled = true;
|
||||||
|
let _CircleIds = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/** Helper method to round values with one digit precision */
|
||||||
|
function round(value) {
|
||||||
|
return Math.round(parseFloat(value) * 10) / 10
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A namespace with static functions to expand and shrink highlighted image regions.
|
||||||
|
* Assumes an SVG image with the following structure:
|
||||||
|
*
|
||||||
|
* <svg viewbox="0 0 100 100">
|
||||||
|
* <!-- The defs section must be defined and cannot be generated in JavaScript -->
|
||||||
|
* <defs>
|
||||||
|
* </defs>
|
||||||
|
* <image width="100" height="100" xlink:href="../assets/chess.jpg"/>
|
||||||
|
* <circle onclick="Highlight.animateZoom(event)" cx="47" cy="18" r="8" stroke-width="0.5" />
|
||||||
|
* <circle onclick="Highlight.animateZoom(event)" cx="60" cy="67" r="8" stroke-width="0.5" />
|
||||||
|
* </svg>
|
||||||
|
*
|
||||||
|
* The SVG root element should use a viewbox with 0 0 100 100 to ensure that the positions and size of the
|
||||||
|
* circles can be represnted in percent.
|
||||||
|
*
|
||||||
|
* @class Highlight
|
||||||
|
* @extends {Object}
|
||||||
|
*/
|
||||||
|
class Highlight extends Object {
|
||||||
|
|
||||||
|
static disableAnimations() {
|
||||||
|
_HighlightEnabled = false;
|
||||||
|
let expanded = document.querySelectorAll('.expanded');
|
||||||
|
for (let obj of expanded) {
|
||||||
|
this.shrink(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enableAnimations() {
|
||||||
|
_HighlightEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeAnimations(svgRoot) {
|
||||||
|
let expanded = svgRoot.querySelectorAll('.expanded');
|
||||||
|
for (let obj of expanded) {
|
||||||
|
TweenLite.set(obj, { scale: 1 });
|
||||||
|
obj.classList.remove('zooming');
|
||||||
|
obj.classList.remove('expanded');
|
||||||
|
}
|
||||||
|
let defs = svgRoot.querySelector('defs');
|
||||||
|
while (defs.firstChild) {
|
||||||
|
defs.firstChild.remove();
|
||||||
|
}
|
||||||
|
let maskImages = svgRoot.querySelectorAll('.addedImage');
|
||||||
|
for (let m of maskImages) {
|
||||||
|
m.remove();
|
||||||
|
}
|
||||||
|
let circles = svgRoot.querySelectorAll('circle');
|
||||||
|
for (let circle of circles) {
|
||||||
|
if (circle.classList.length == 0) {
|
||||||
|
circle.removeAttribute('class');
|
||||||
|
}
|
||||||
|
if (circle.hasAttribute('id') && circle.getAttribute('id').startsWith('@@')) {
|
||||||
|
circle.removeAttribute('id');
|
||||||
|
}
|
||||||
|
circle.removeAttribute('data-svg-origin');
|
||||||
|
circle.removeAttribute('transform');
|
||||||
|
circle.removeAttribute('style');
|
||||||
|
let cx = circle.getAttribute('cx');
|
||||||
|
let cy = circle.getAttribute('cy');
|
||||||
|
let r = circle.getAttribute('r');
|
||||||
|
circle.setAttribute('cx', round(cx));
|
||||||
|
circle.setAttribute('cy', round(cy));
|
||||||
|
circle.setAttribute('r', round(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) {
|
||||||
|
if (obj == null)
|
||||||
|
return
|
||||||
|
//console.log("expand")
|
||||||
|
obj.classList.add('zooming');
|
||||||
|
TweenLite.to(obj, duration, {
|
||||||
|
scale: scale,
|
||||||
|
onUpdate: () => {
|
||||||
|
let scale = obj._gsTransform.scaleX;
|
||||||
|
obj.setAttribute('stroke-width', stroke / scale);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
console.log('expand complete');
|
||||||
|
obj.classList.remove('zooming');
|
||||||
|
obj.classList.add('expanded');
|
||||||
|
obj.setAttribute('stroke-width', stroke / scale);
|
||||||
|
if (onComplete) onComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static shrink(obj, { duration = 0.5, stroke = 2 } = {}) {
|
||||||
|
//console.log("shrink")
|
||||||
|
if (obj == null)
|
||||||
|
return
|
||||||
|
obj.classList.add('zooming');
|
||||||
|
TweenLite.to(obj, duration, {
|
||||||
|
scale: 1,
|
||||||
|
onUpdate: () => {
|
||||||
|
let scale = obj._gsTransform.scaleX;
|
||||||
|
obj.setAttribute('stroke-width', stroke / scale);
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
//console.log("shrink complete")
|
||||||
|
obj.classList.remove('zooming');
|
||||||
|
obj.classList.remove('expanded');
|
||||||
|
obj.setAttribute('stroke-width', stroke);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static animateCircle(target, callback) {
|
||||||
|
console.log('ANIMATE CIRCLE', this);
|
||||||
|
// ** DEBUG OUTPUTS **
|
||||||
|
|
||||||
|
let circle = target;
|
||||||
|
// We need a unique id to ensure correspondence between circle, mask, and maskImage
|
||||||
|
if (!circle.hasAttribute('id')) {
|
||||||
|
_CircleIds += 1;
|
||||||
|
circle.setAttribute('id', '@@' + _CircleIds);
|
||||||
|
}
|
||||||
|
let id = circle.getAttribute('id');
|
||||||
|
TweenLite.set(circle, { transformOrigin: '50% 50%' });
|
||||||
|
/*if (circle.classList.contains('zooming')) {
|
||||||
|
console.log("already zooming")
|
||||||
|
return
|
||||||
|
}*/
|
||||||
|
|
||||||
|
let svgRoot = circle.closest('svg');
|
||||||
|
let circleGroup = circle.parentNode;
|
||||||
|
let image = svgRoot.querySelector('image');
|
||||||
|
|
||||||
|
|
||||||
|
let stroke = parseFloat(circleGroup.getAttribute('stroke-width') || 6);
|
||||||
|
|
||||||
|
let defs = svgRoot.querySelector('defs');
|
||||||
|
if (defs == null) {
|
||||||
|
defs = document.createElementNS(svgRoot, 'defs');
|
||||||
|
svgRoot.insertBefore(defs, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // We need direct children, therefore we cannot use querySelectorAll
|
||||||
|
let maskImageId = 'maskImage' + id;
|
||||||
|
let maskImage = svgRoot.getElementById(maskImageId);
|
||||||
|
|
||||||
|
if (circle.classList.contains('expanded')) {
|
||||||
|
if (!circle.classList.contains('zooming')) {
|
||||||
|
this.shrink(circle, { stroke });
|
||||||
|
this.shrink(maskImage, { stroke });
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//console.log("animate called while zooming out -> expand")
|
||||||
|
}
|
||||||
|
else if (circle.classList.contains('zooming')) {
|
||||||
|
//console.log("animate called while zooming in -> shrink")
|
||||||
|
this.shrink(circle, { stroke });
|
||||||
|
this.shrink(maskImage, { stroke });
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let circles = Array.from(circleGroup.children).filter(e => e.tagName == 'circle');
|
||||||
|
for (let c of circles) {
|
||||||
|
//console.log("shrinking all circles")
|
||||||
|
this.shrink(c, { stroke });
|
||||||
|
}
|
||||||
|
let maskImages = circleGroup.querySelectorAll('.addedImage');
|
||||||
|
for (let m of maskImages) {
|
||||||
|
this.shrink(m, { stroke });
|
||||||
|
}
|
||||||
|
|
||||||
|
Highlight._createSVGMask(svgRoot, image, id);
|
||||||
|
|
||||||
|
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
|
||||||
|
|
||||||
|
this.expand(circle, { stroke, onComplete: callback });
|
||||||
|
this.expand(maskImage);
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
static openHighlight(target, {
|
||||||
|
animation = 0.5,
|
||||||
|
scale = 2,
|
||||||
|
onExpanded = null
|
||||||
|
} = {}) {
|
||||||
|
|
||||||
|
console.log('Open Highlight!', target);
|
||||||
|
|
||||||
|
if (Highlight._isExpanded(target)) {
|
||||||
|
console.log('Target is already expanded!');
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
|
||||||
|
let targetId = target.getAttribute('id');
|
||||||
|
console.log(targetId, targetId && targetId.startsWith('@@'));
|
||||||
|
if(targetId && targetId.startsWith('@@')){
|
||||||
|
let id = targetId.slice(2);
|
||||||
|
const imageId = '#maskImage'+id;
|
||||||
|
const parent = target.parentNode;
|
||||||
|
if(parent != null){
|
||||||
|
let image = parent.querySelector(imageId);
|
||||||
|
if(image){
|
||||||
|
this._bringToFront(image);
|
||||||
|
}else console.error('Could not find corresponding image element.');
|
||||||
|
}else console.log('Element was no parent:', target);
|
||||||
|
}
|
||||||
|
console.log("_bringToFront");
|
||||||
|
this._bringToFront(target);
|
||||||
|
|
||||||
|
let svgRoot = target.closest('svg');
|
||||||
|
let image = svgRoot.querySelector('image');
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
let [mask, maskImage] = Highlight._getSVGMask(target, { svgRoot, image });
|
||||||
|
|
||||||
|
console.log({svgRoot, image, mask, maskImage});
|
||||||
|
let center = Highlight._calculateCenterRelativeTo(target, image);
|
||||||
|
|
||||||
|
TweenLite.set(maskImage, { transformOrigin: `${center.x}% ${center.y}%` });
|
||||||
|
TweenLite.set(target, { transformOrigin: '50% 50%' });
|
||||||
|
|
||||||
|
TweenLite.to([target, maskImage], animation, {
|
||||||
|
scale,
|
||||||
|
onComplete: onExpanded
|
||||||
|
});
|
||||||
|
|
||||||
|
target.classList.add('expanded');
|
||||||
|
console.log({target, maskImage, scale, animation});
|
||||||
|
console.log(maskImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
static _bringToFront(target){
|
||||||
|
const parent = target.parentNode;
|
||||||
|
if(target && parent){
|
||||||
|
parent.removeChild(target);
|
||||||
|
parent.appendChild(target);
|
||||||
|
}else console.error('Could not bring to front. Either no target or no parent.', target, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _getSVGMask(circle, { svgRoot = null, image = null } = {}) {
|
||||||
|
const id = this._retrieveId(circle);
|
||||||
|
const maskId = 'mask' + id;
|
||||||
|
const maskImageId = 'maskImage' + id;
|
||||||
|
|
||||||
|
if (!svgRoot) svgRoot = circle.closest('svg');
|
||||||
|
|
||||||
|
let mask = svgRoot.getElementById(maskId);
|
||||||
|
let maskImage = svgRoot.getElementById(maskImageId);
|
||||||
|
|
||||||
|
if (!mask || !maskImage)
|
||||||
|
[mask, maskImage] = Highlight._createSVGMask(circle, { svgRoot, image, id });
|
||||||
|
|
||||||
|
return [mask, maskImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SVG mask for a provided svgElement.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @param {SVGElement} element - Element that should be masked.
|
||||||
|
* @param {object} opts - Optional parameters to avoid unnecessary fetching of elements.
|
||||||
|
* @param {SVGElement} opts.svgRoot - The root <svg> element of the element.
|
||||||
|
* @param {SVGImageElement} opts.image - The image that is used in the mask.
|
||||||
|
* @param {number} opts.id - The id of the mask.
|
||||||
|
* @returns
|
||||||
|
* @memberof Highlight
|
||||||
|
*/
|
||||||
|
static _createSVGMask(element, { svgRoot = null, image = null, id = null } = {}) {
|
||||||
|
|
||||||
|
// We can fetch these values here, but it's more efficient to
|
||||||
|
// simply pass them in, as it's likely they were already retrieved beforehand.
|
||||||
|
if (svgRoot == null) svgRoot = element.closest('svg');
|
||||||
|
if (image == null) image = svgRoot.querySelector('image');
|
||||||
|
if (id == null) id = this._retrieveId(element);
|
||||||
|
|
||||||
|
let svg = 'http://www.w3.org/2000/svg';
|
||||||
|
let xlink = 'http://www.w3.org/1999/xlink';
|
||||||
|
|
||||||
|
let svgGroup = element.parentNode;
|
||||||
|
|
||||||
|
let src = image.getAttributeNS(xlink, 'href');
|
||||||
|
|
||||||
|
let maskId = 'mask' + id;
|
||||||
|
let maskImageId = 'maskImage' + id;
|
||||||
|
let mask = svgRoot.getElementById(maskId);
|
||||||
|
let maskImage = svgRoot.getElementById(maskImageId);
|
||||||
|
|
||||||
|
let defs = svgRoot.querySelector('defs');
|
||||||
|
if (defs == null) {
|
||||||
|
defs = document.createElementNS(svgRoot, 'defs');
|
||||||
|
svgRoot.insertBefore(defs, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask == null) {
|
||||||
|
mask = document.createElementNS(svg, 'mask');
|
||||||
|
mask.setAttribute('id', maskId);
|
||||||
|
let maskCircle = element.cloneNode(true);
|
||||||
|
mask.appendChild(maskCircle);
|
||||||
|
defs.appendChild(mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bbox = svgRoot.getElementsByTagName('image')[0].getBBox();
|
||||||
|
let width = bbox.width;
|
||||||
|
let height = bbox.height;
|
||||||
|
|
||||||
|
if (maskImage == null) {
|
||||||
|
|
||||||
|
maskImage = document.createElementNS(svg, 'image');
|
||||||
|
maskImage.style.pointerEvents = 'none';
|
||||||
|
maskImage.setAttribute('id', maskImageId);
|
||||||
|
maskImage.setAttributeNS(xlink, 'href', src);
|
||||||
|
maskImage.setAttribute('width', width);
|
||||||
|
maskImage.setAttribute('height', height);
|
||||||
|
maskImage.setAttribute('class', 'addedImage');
|
||||||
|
svgGroup.insertBefore(maskImage, element); // image.nextSibling)
|
||||||
|
TweenLite.set(maskImage, { scale: 1 });
|
||||||
|
maskImage.style.mask = 'url(#' + maskId + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
svgGroup.appendChild(maskImage);
|
||||||
|
// svgGroup.appendChild(element)
|
||||||
|
|
||||||
|
return [mask, maskImage]
|
||||||
|
}
|
||||||
|
|
||||||
|
static _calculateCenterRelativeTo(target, image) {
|
||||||
|
let bbox = image.getBBox();
|
||||||
|
let width = bbox.width;
|
||||||
|
let height = bbox.height;
|
||||||
|
let cx = target.getAttribute('cx');
|
||||||
|
let cy = target.getAttribute('cy');
|
||||||
|
return { x: (cx / width) * 100, y: (cy / height) * 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
static _isExpanded(target) {
|
||||||
|
return target.classList.contains(Highlight.expandedClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
static _setExpanded(target) {
|
||||||
|
target.classList.add(Highlight.expandedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _notExpanded(target) {
|
||||||
|
target.classList.remove(Highlight.expandedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static closeHighlight(target, { animation = 0.5 } = {}) {
|
||||||
|
console.log('Close Highlight');
|
||||||
|
Highlight._notExpanded(target);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
let [mask, maskImage] = Highlight._getSVGMask(target);
|
||||||
|
|
||||||
|
TweenLite.to([target, maskImage], animation, {
|
||||||
|
scale: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static animate(event) {
|
||||||
|
if (!_HighlightEnabled)
|
||||||
|
return
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
Highlight.animateCircle(event.target);
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
static _retrieveId(target) {
|
||||||
|
let id = target.getAttribute('id');
|
||||||
|
// We need a unique id to ensure correspondence between circle, mask, and maskImage
|
||||||
|
if (!id) {
|
||||||
|
_CircleIds += 1;
|
||||||
|
target.setAttribute('id', '@@' + _CircleIds);
|
||||||
|
id = _CircleIds;
|
||||||
|
} else {
|
||||||
|
id = parseInt(id.substring(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Highlight.expandedClass = 'expanded';
|
||||||
|
|
||||||
/* Needed to ensure that rollup.js includes class definitions and the classes
|
/* Needed to ensure that rollup.js includes class definitions and the classes
|
||||||
are visible inside doctests.
|
are visible inside doctests.
|
||||||
@ -7782,5 +8168,6 @@
|
|||||||
window.randomFloat = randomFloat;
|
window.randomFloat = randomFloat;
|
||||||
|
|
||||||
window.CardWrapper = CardWrapper;
|
window.CardWrapper = CardWrapper;
|
||||||
|
window.Highlight = Highlight;
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
@ -20,6 +20,9 @@ import {Cycle, Colors, Elements, Angle, Dates, Points, Polygon, Rect, Sets, Stri
|
|||||||
import UITest from './uitest.js'
|
import UITest from './uitest.js'
|
||||||
|
|
||||||
import {CardWrapper} from './card/wrapper.js'
|
import {CardWrapper} from './card/wrapper.js'
|
||||||
|
import {Highlight} from './card/highlight.js'
|
||||||
|
|
||||||
|
|
||||||
/* Needed to ensure that rollup.js includes class definitions and the classes
|
/* Needed to ensure that rollup.js includes class definitions and the classes
|
||||||
are visible inside doctests.
|
are visible inside doctests.
|
||||||
*/
|
*/
|
||||||
@ -85,3 +88,4 @@ window.randomInt = randomInt
|
|||||||
window.randomFloat = randomFloat
|
window.randomFloat = randomFloat
|
||||||
|
|
||||||
window.CardWrapper = CardWrapper
|
window.CardWrapper = CardWrapper
|
||||||
|
window.Highlight = Highlight
|
@ -30,7 +30,7 @@ function round(value) {
|
|||||||
* @class Highlight
|
* @class Highlight
|
||||||
* @extends {Object}
|
* @extends {Object}
|
||||||
*/
|
*/
|
||||||
class Highlight extends Object {
|
export class Highlight extends Object {
|
||||||
|
|
||||||
static disableAnimations() {
|
static disableAnimations() {
|
||||||
_HighlightEnabled = false
|
_HighlightEnabled = false
|
||||||
@ -194,7 +194,7 @@ class Highlight extends Object {
|
|||||||
onExpanded = null
|
onExpanded = null
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
|
||||||
console.log('Open Highlight!')
|
console.log('Open Highlight!', target)
|
||||||
|
|
||||||
if (Highlight._isExpanded(target)) {
|
if (Highlight._isExpanded(target)) {
|
||||||
console.log('Target is already expanded!')
|
console.log('Target is already expanded!')
|
||||||
@ -202,7 +202,7 @@ class Highlight extends Object {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
let targetId = target.getAttribute('id')
|
let targetId = target.getAttribute('id')
|
||||||
console.log(targetId)
|
console.log(targetId, targetId && targetId.startsWith('@@'))
|
||||||
if(targetId && targetId.startsWith('@@')){
|
if(targetId && targetId.startsWith('@@')){
|
||||||
let id = targetId.slice(2)
|
let id = targetId.slice(2)
|
||||||
const imageId = '#maskImage'+id
|
const imageId = '#maskImage'+id
|
||||||
@ -214,7 +214,7 @@ class Highlight extends Object {
|
|||||||
}else console.error('Could not find corresponding image element.')
|
}else console.error('Could not find corresponding image element.')
|
||||||
}else console.log('Element was no parent:', target)
|
}else console.log('Element was no parent:', target)
|
||||||
}
|
}
|
||||||
|
console.log("_bringToFront")
|
||||||
this._bringToFront(target)
|
this._bringToFront(target)
|
||||||
|
|
||||||
let svgRoot = target.closest('svg')
|
let svgRoot = target.closest('svg')
|
||||||
@ -222,21 +222,21 @@ class Highlight extends Object {
|
|||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
let [mask, maskImage] = Highlight._getSVGMask(target, { svgRoot, image })
|
let [mask, maskImage] = Highlight._getSVGMask(target, { svgRoot, image })
|
||||||
|
|
||||||
|
console.log({svgRoot, image, mask, maskImage})
|
||||||
let center = Highlight._calculateCenterRelativeTo(target, image)
|
let center = Highlight._calculateCenterRelativeTo(target, image)
|
||||||
|
|
||||||
TweenLite.set(maskImage, { transformOrigin: `${center.x}% ${center.y}%` })
|
TweenLite.set(maskImage, { transformOrigin: `${center.x}% ${center.y}%` })
|
||||||
TweenLite.set(target, { transformOrigin: '50% 50%' })
|
TweenLite.set(target, { transformOrigin: '50% 50%' })
|
||||||
|
|
||||||
TweenLite.to(target, 2, {
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
TweenLite.to([target, maskImage], animation, {
|
TweenLite.to([target, maskImage], animation, {
|
||||||
scale,
|
scale,
|
||||||
onComplete: onExpanded
|
onComplete: onExpanded
|
||||||
})
|
})
|
||||||
|
|
||||||
target.classList.add('expanded')
|
target.classList.add('expanded')
|
||||||
|
console.log({target, maskImage, scale, animation})
|
||||||
|
console.log(maskImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -397,4 +397,4 @@ class Highlight extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Highlight.expandedClass = 'expanded'
|
Highlight.expandedClass = 'expanded'
|
||||||
window.Highlight = Highlight
|
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
<h1 style="color: gray;">A Demo Card with onclick</h1>
|
<h1 style="color: gray;">A Demo Card with onclick</h1>
|
||||||
<figure style="position: relative;">
|
<figure style="position: relative;">
|
||||||
<img width="75%" src="../examples/women.jpeg">
|
<img width="75%" src="../examples/women.jpeg">
|
||||||
<svg id="overlayBase" style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
|
<svg class="overlayBase" style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
|
||||||
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
||||||
<circle cx="35" cy="50" r="35" class="highlight" onclick="alert('Highlight clicked')" stroke="white"
|
<circle cx="35" cy="50" r="35" class="highlight" onclick="alert('Highlight clicked')" stroke="white"
|
||||||
fill="transparent" stroke-width="1" />
|
fill="transparent" stroke-width="1" />
|
||||||
@ -59,15 +59,23 @@
|
|||||||
<article id="demoCardWithSelector"
|
<article id="demoCardWithSelector"
|
||||||
style="position: absolute; left: 50%; top: 0%; padding: 16px; margin: 16px; border: 1px solid gray; width: 320px; height: 240px;">
|
style="position: absolute; left: 50%; top: 0%; padding: 16px; margin: 16px; border: 1px solid gray; width: 320px; height: 240px;">
|
||||||
<h1 style="color: gray;">A Demo Card with selectors</h1>
|
<h1 style="color: gray;">A Demo Card with selectors</h1>
|
||||||
<figure style="position: relative;">
|
|
||||||
<img width="75%" src="../examples/king.jpeg">
|
<!-- Grr... The viewBox must reflect the width & height, using 0, 0, 100,
|
||||||
<svg id="overlayBase" style="position: absolute; left: 0px; top: 0px; width: 100%; height: 100%;"
|
100 leads to blank spaces -->
|
||||||
viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
|
<svg class="overlayBase" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 268 188"
|
||||||
<circle cx="35" cy="50" r="35" class="highlight" stroke="white" fill="transparent"
|
style="width: 180px; height: 100px;" preserveAspectRatio="xMidYMid meet">
|
||||||
stroke-width="1" />
|
<defs>
|
||||||
</svg>
|
</defs>
|
||||||
</figure>
|
<image xlink:href="../examples/king.jpeg" x="0" y="0" height="188" width="268" />
|
||||||
<p>Lorem ipsum <a class="link" href="javascript:alert('Link clicked via href')" style="color:blue;">dolor</a> sit
|
<g>
|
||||||
|
<circle cx="50%" cy="50%" r="25%" class="highlight" stroke="white" fill="transparent"
|
||||||
|
stroke-width="1%" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<p>Lorem ipsum <a class="link" href="javascript:alert('Link clicked via href')"
|
||||||
|
style="color:blue;">dolor</a> sit
|
||||||
amet,
|
amet,
|
||||||
consetetur sadipscing elitr.</p>
|
consetetur sadipscing elitr.</p>
|
||||||
</article>
|
</article>
|
||||||
@ -84,8 +92,17 @@
|
|||||||
const wrapper2 = new CardWrapper(demoCardWithSelector)
|
const wrapper2 = new CardWrapper(demoCardWithSelector)
|
||||||
wrapper2.handleClicksAsTaps()
|
wrapper2.handleClicksAsTaps()
|
||||||
wrapper2.onTap('.highlight', event => {
|
wrapper2.onTap('.highlight', event => {
|
||||||
alert('.highlight clicked')
|
|
||||||
|
Highlight.openHighlight(event.target, {
|
||||||
|
|
||||||
|
onExpanded: () => {
|
||||||
|
console.log("onExpanded", event.target)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Highlight.animate(event)
|
||||||
|
|
||||||
wrapper2.onTap('.link', event => {
|
wrapper2.onTap('.link', event => {
|
||||||
alert('.link clicked')
|
alert('.link clicked')
|
||||||
})
|
})
|
||||||
@ -94,7 +111,8 @@
|
|||||||
<h2>
|
<h2>
|
||||||
Using Cards within Scatters
|
Using Cards within Scatters
|
||||||
</h2>
|
</h2>
|
||||||
<p>Cards can be used within scatters. Since the <code>CardWrapper</code> implements the <code>ITapDelegate</code> protocol they can simply
|
<p>Cards can be used within scatters. Since the <code>CardWrapper</code> implements the <code>ITapDelegate</code>
|
||||||
|
protocol they can simply
|
||||||
be attached to a DOMScatter object. See the <a href="../scatter.html">Scatter Doctest</a>.
|
be attached to a DOMScatter object. See the <a href="../scatter.html">Scatter Doctest</a>.
|
||||||
</p>
|
</p>
|
||||||
<div class="interactive grayBorder" style="position: relative;">
|
<div class="interactive grayBorder" style="position: relative;">
|
||||||
@ -104,6 +122,7 @@
|
|||||||
References
|
References
|
||||||
</h2>
|
</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="https://uicookies.com/css-card-design/">30 Visually Stunning CSS Cards Inspirations For Every Type Of Websites 2019</a></li>
|
<li><a href="https://uicookies.com/css-card-design/">30 Visually Stunning CSS Cards Inspirations For Every Type
|
||||||
|
Of Websites 2019</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
@ -17,11 +17,6 @@ export class CardWrapper extends Object {
|
|||||||
|
|
||||||
handleClicks() {
|
handleClicks() {
|
||||||
this.domNode.addEventListener('click', event => {
|
this.domNode.addEventListener('click', event => {
|
||||||
console.log('handleClicks', event.isTrusted)
|
|
||||||
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
|
||||||
Therefore we make an exception and let the original click event through.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
Events.stop(event)
|
Events.stop(event)
|
||||||
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
if (this.triggerSVGClicks && this.isSVGNode(event.target)) {
|
||||||
@ -34,10 +29,6 @@ export class CardWrapper extends Object {
|
|||||||
|
|
||||||
handleClicksAsTaps() {
|
handleClicksAsTaps() {
|
||||||
this.domNode.addEventListener('click', event => {
|
this.domNode.addEventListener('click', event => {
|
||||||
console.log('handleClicksAsTaps', event.isTrusted)
|
|
||||||
/* Currently we cannot send synthesized click events to SVG elements without unwanted side effects.
|
|
||||||
Therefore we make an exception and let the original click event through.
|
|
||||||
*/
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
Events.stop(event)
|
Events.stop(event)
|
||||||
|
|
||||||
@ -62,9 +53,7 @@ export class CardWrapper extends Object {
|
|||||||
if (this.tapNodes.has(node))
|
if (this.tapNodes.has(node))
|
||||||
return true
|
return true
|
||||||
for (let [selector, handler] of this.tapHandler.entries()) {
|
for (let [selector, handler] of this.tapHandler.entries()) {
|
||||||
console.log("selector", this.domNode.querySelectorAll(selector))
|
|
||||||
for (let obj of this.domNode.querySelectorAll(selector)) {
|
for (let obj of this.domNode.querySelectorAll(selector)) {
|
||||||
console.log("selector2", node, obj)
|
|
||||||
if (node == obj) {
|
if (node == obj) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -127,7 +116,6 @@ export class CardWrapper extends Object {
|
|||||||
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
|
||||||
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
|
||||||
Therefore we use the original click event (see constructor). */
|
Therefore we use the original click event (see constructor). */
|
||||||
console.log("simulateClick", node, node.ownerSVGElement)
|
|
||||||
if (this.isSVGNode(node)) {
|
if (this.isSVGNode(node)) {
|
||||||
if (this.triggerSVGClicks) {
|
if (this.triggerSVGClicks) {
|
||||||
let click = new Event('click')
|
let click = new Event('click')
|
||||||
@ -160,7 +148,6 @@ export class CardWrapper extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tap(event, calledBy='unknown') {
|
tap(event, calledBy='unknown') {
|
||||||
console.log("tap", calledBy, event.alreadyTapped, event)
|
|
||||||
if (event.isTrusted) {
|
if (event.isTrusted) {
|
||||||
let node = this.nearestActive(event)
|
let node = this.nearestActive(event)
|
||||||
this.nodeTapped(node, event)
|
this.nodeTapped(node, event)
|
||||||
@ -184,4 +171,3 @@ export class CardWrapper extends Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
window.CardWrapper = CardWrapper
|
|
||||||
|
Loading…
Reference in New Issue
Block a user