1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-17 15:24:50 +00:00
TiddlyWiki5/plugins/tiddlywiki/dynannotate/modules/element-spotlight.js
Jeremy Ruston a9f9ffd409
Introduce Tour Plugin and Confetti Plugin, improve Dynannotate Plugin (#7734)
* First commit

* Typo

* Add support for delay parameter

* Add confetti widget

* Add tour plugin

* Add element spotlight to dynannotate plugin

Useful for highlighting on screen elements for the user

* More and bigger confetti by default

* Use new element spotlight to provide hints

* Adjust hint selectors for create tiddler tour step

* Include confetti plugin in prerelease

* Clarify wording of confetti demo

* Don't link TiddlyWiki in the tour panel

* Tweaks to tour buttons

* Mark dependents of the tour plugin

* Add full screen section of tour and tour edition

* Remove Anna Freud references from welcome tiddler

* Build the tour edition in the preview

* Fix typo in build script

* Populate tour edition with solar system data

From Simple English Wikipedia

* Missing tag

* Add page control button to start tour

Also make the tour controls visible in full screen mode

* Refactor to use global procedures to control the tour

* Change "startup-actions" field to "enter-actions" to avoid confusion

* Add a tour logo

* Refactor to allow multiple tours to be loaded at once

* Remove wikification from welcome tour step

* Update docs

* Simplify styles for top bar

* Tours should have a $:/tags/Tour tag

* Tour should autostart in the tour edition, but not in the main wiki

* Better labelling for the main preview

* Fix build process

We build a separate tour.html wiki, but can include the tour in other wikis too

* Remove obsolete text

* Add "using tags" as a separate tour

* Remove old debugging code

* Add tour chooser

* Ensure that the current tour isn't listed as an option in the final step

* Use whitespace trim

Note that the setting is inherited by procedure and widget definitions

* Simplify tour step format

* Remove obsolete state tiddler

Not needed because now we initialise it in startup actions

* Fix gap between navigation buttons

* Clean up tiddler titles within the introduction tour

* Finish allowing the name "TiddlyWiki" to be customised

Some of the code was in the previous commit. Next we'll wire up the user interface

* Clarify docs

* Add a settings pane giving a birds eye view of a tour

* Avoid having to embed confetti in the final step

* Update docs

* Tweak styling of tour chooser dropdown

* Add a button to launch tour steps directly, and give them captions

* Expose custom tour settings

* Use the tour step caption as the heading

* Fix initialisation when jumping to a tour step

* Introduce step about tags

* Improve wording

* Improve styling of task call-to-action and nav buttons

* Adopt new conditional shortcut syntax

* Wording and ordering tweaks

* Fix typos

Thanks @pmario

* Simplify styling of tour overlay

* Use custom palette colours

Makes it easier for people to use their own colour scheme for the tour

* More custom colours

* Tour wording tweaks

* Extends the tour plugin with a condition field (#7861)

* feat: support condition field to determine whether a step should be shown

* feat: add support for overriding the hint text using the field 'hint' from the step tiddler

* fix: roll back tour display procedure for now until an override mechanism has been discussed

* fix: renamed advance-criterion field and associated variables to step-success-filter

* fix: renamed hint field to hint-text and selector to hint-selector

* refactor: to create function to get all tour tiddlers filtered by their condition field

* refactor: rename globals tiddlers to variables and avoid making any of the tour procedures global

* fix: also rename globals.tid file to variables.tid

* docs: cover all tour steps tiddler fields

* fix: improve spacing in Tour HUD

* WIP

---------

Co-authored-by: Jeremy Ruston <174761+Jermolene@users.noreply.github.com>
Co-authored-by: Saq Imtiaz <saq.imtiaz@gmail.com>
2024-01-25 12:53:35 +00:00

137 lines
4.0 KiB
JavaScript

/*\
title: $:/plugins/tiddlywiki/dynannotate/element-spotlight.js
type: application/javascript
module-type: library
Manages the element spotlight effect
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
function ElementSpotlight() {
this.animationStartTime; // Undefined if no animation is in progress
// Create DOM nodes
this.spotlightElement = $tw.utils.domMaker("div",{
"class": "tc-dynannotate-spotlight"
});
this.spotlightWrapper = $tw.utils.domMaker("div",{
"class": "tc-dynannotate-spotlight-wrapper",
children: [
this.spotlightElement
]
});
document.body.appendChild(this.spotlightWrapper);
}
/*
Return the first visible element that matches a selector
*/
ElementSpotlight.prototype.querySelectorSafe = function(selector) {
var targetNodes;
// Get the matching elements
try {
targetNodes = document.querySelectorAll(selector);
} catch(e) {
console.log("Error with selector: " + selector);
}
if(!targetNodes) {
return undefined;
}
// Remove any elements from the start of the list that are hidden, or have hidden ancestors
var didRemoveFirstEntry;
do {
didRemoveFirstEntry = false;
var hasHiddenAncestor = false,
n = targetNodes[0];
while(n) {
if(n.hidden || (n instanceof Element && window.getComputedStyle(n).display === "none")) {
hasHiddenAncestor = true;
break;
}
n = n.parentNode;
}
if(hasHiddenAncestor) {
// Remove first entry from targetNodes array
targetNodes = [].slice.call(targetNodes, 1);
didRemoveFirstEntry = true;
}
} while(didRemoveFirstEntry)
// Return the first result
return targetNodes[0];
};
ElementSpotlight.prototype.positionSpotlight = function(x,y,innerRadius,outerRadius,opacity) {
this.spotlightElement.style.display = "block";
this.spotlightElement.style.backgroundImage = "radial-gradient(circle at " + (x / window.innerWidth * 100) + "% " + (y / window.innerHeight * 100) + "%, transparent " + innerRadius + "px, rgba(0, 0, 0, " + opacity + ") " + outerRadius + "px)";
};
ElementSpotlight.prototype.easeInOut = function(v) {
return (Math.sin((v - 0.5) * Math.PI) + 1) / 2;
};
/*
Shine a spotlight on the first element that matches an array of selectors
*/
ElementSpotlight.prototype.shineSpotlight = function(selectors) {
var self = this;
function animationLoop(selectors) {
// Calculate how far through the animation we are
// 0...1 = zoom in
// 1...2 = hold
// 2...3 = fade out
var now = new Date(),
t = (now - self.animationStartTime) / ($tw.utils.getAnimationDuration() * 2);
t = t >= 3 ? 3 : t;
// Query the selector for the target element
var targetNode, selectorIndex = 0;
while(!targetNode && selectorIndex < selectors.length) {
targetNode = self.querySelectorSafe(selectors[selectorIndex]);
selectorIndex += 1;
}
// Position the spotlight if we've got the target
if(targetNode) {
var rect = targetNode.getBoundingClientRect();
var innerRadius, outerRadius, opacity;
if(t <= 1) {
t = self.easeInOut(t);
innerRadius = rect.width / 2 + (window.innerWidth * 2 * (1 - t));
outerRadius = rect.width + (window.innerWidth * 3 * (1 - t));
opacity = 0.2 + t / 4;
} else if(t <= 2) {
innerRadius = rect.width / 2;
outerRadius = rect.width;
opacity = 0.45;
} else {
t = self.easeInOut(3 - t);
innerRadius = rect.width / 2 + (window.innerWidth * 2 * (1 - t));
outerRadius = rect.width + (window.innerWidth * 3 * (1 - t));
opacity = t / 3;
}
self.positionSpotlight((rect.left + rect.right) / 2,(rect.top + rect.bottom) / 2,innerRadius,outerRadius,opacity);
} else {
self.spotlightElement.style.display = "none";
}
// Call the next frame unless we're at the end
if(t <= 3) {
window.requestAnimationFrame(function () {
animationLoop(selectors);
});
} else {
// End the animation if we've exceeded the time limit
self.animationStartTime = undefined;
}
}
this.animationStartTime = new Date();
window.requestAnimationFrame(function () {
animationLoop(selectors);
});
};
exports.ElementSpotlight = ElementSpotlight;
})();