Commit 53cfcd7f authored by Leon Arnott's avatar Leon Arnott
Browse files

Added (animate:), a command to re-apply a transition to an already existing hook.

This was added simply because I noticed people could use (t8n:) and (rerun:) to accomplish this, and I wanted this little hack to have a proper idiom. As it is, it isn't too useful due to
the current suite of transitions involve the hook slowly appearing (with "rumble" and "shudder" notable exceptions), but maybe a few more could be added.

As it is, I added one more transition: "blur", which uses the new CSS filters to make a hook appear from a blur.

Also fixed Utils.childrenProbablyInline() a bit more.
parent e918daa6aa26
......@@ -187,6 +187,7 @@ Documentation is at http://twine2.neocities.org/. See below for compilation inst
* "zoom", which makes the transitioning entity zoom in or out from the mouse cursor's position (or the last place the touch device was touched).
* "fade-up", "fade-down", "fade-left", and "fade-right", which are combinations of "dissolve" with gentler sliding transitions.
* "fade", which is an alias for "dissolve" (for consistency with the above, as well as the "fade-in-out" text style).
* "blur", which uses the new CSS blur filter to make a hook appear or disappear in a blur. Note that this will not work in any version of Internet Explorer.
* Added `(append-with:)`, `(prepend-with:)` and `(replace-with:)`, changers which modify the hook by adding a given string of code to the start or end of it, or replace it entirely. This is intended for use with things like dialogue tags or punctuation that's commonly used throughout the story.
* Added two debugging changers, `(test-true:)` and `(test-false:)`, inspired by Chapbook's `ifalways` and `ifnever` mods, that ignore all of the values given to them (as well as their data types) and instead act like `(if:true)` or `(if:false)`, respectively. As with their inspirations, these are designed for quick debugging checks.
......@@ -198,6 +199,7 @@ Documentation is at http://twine2.neocities.org/. See below for compilation inst
* Added a `(meter:)` macro, used for creating a videogame bar-graph meter that's bound to a numeric variable. You can provide this with a colour or gradient for the bar, and a text label, as well as make the bar left-aligned, right-aligned or centered.
* Added a `(link-rerun:)` macro, which is similar to `(link-repeat:)`, but which replaces the hook on each click rather than appending to it.
* Added a `(rerun:)` macro, which replaces a given hook with its original source, eliminating any changes made to it by `(replace:)` or other macros. This also runs any macros inside the hook an additional time.
* Added an `(animate:)` macro, which causes an already-present hook to transition in again, using a specified transition and time.
* Added a `(hide:)` macro, which removes the contents of a given hook from the passage, but allows the `(show:)` macro to restore the contents later. Hooks hidden with `(hide:)` will not re-run any containing macros when `(show:)` is used on them later.
* Added a `(seq-link:)` macro, a variation of `(cycling-link:)` which does not cycle - it simply turns into plain text on the final string.
* Added a `(verbatim-print:)` command macro (alias `(v6m-print:)`), a combination of `(verbatim:)` and `(print:)`. This is designed especially for printing player-inputted strings that may, for whatever reason, contain markup.
......
......@@ -2057,7 +2057,7 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
by (link-reveal:) or (click:), you may find the (more:) macro to be convenient.
See also:
(hidden:), (replace:), (rerun:), (more:)
(hidden:), (replace:), (rerun:), (more:), (animate:)
Added in: 2.0.0
#showing and hiding
......@@ -2106,7 +2106,7 @@ define(['jquery', 'macros', 'utils', 'state', 'passages', 'renderer', 'engine',
If you want to rerun a hook multiple times based on elapsed real time, use the (live:) macro.
See also:
(replace:), (show:), (more:), (live:)
(replace:), (show:), (more:), (live:), (animate:)
Added in: 3.2.0
#revision
......
"use strict";
define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'datatypes/gradient', 'datatypes/changercommand', 'datatypes/lambda', 'internaltypes/changedescriptor', 'internaltypes/twineerror'],
($, Macros, Utils, {geomParse, geomStringRegExp}, Colour, Gradient, ChangerCommand, Lambda, ChangeDescriptor, TwineError) => {
define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'datatypes/hookset', 'datatypes/gradient', 'datatypes/changercommand', 'datatypes/lambda', 'internaltypes/changedescriptor', 'internaltypes/twineerror'],
($, Macros, Utils, {geomParse, geomStringRegExp}, Colour, HookSet, Gradient, ChangerCommand, Lambda, ChangeDescriptor, TwineError) => {
/*
Built-in hook style changer macros.
......@@ -41,6 +41,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
```
*/
const
{assign} = Object,
{either, wrapped, optional, Any, Everything, zeroOrMore, rest, insensitiveSet, positiveNumber, positiveInteger, nonNegativeNumber, percent} = Macros.TypeSignature,
IfTypeSignature = [wrapped(Boolean, "If you gave a number, you may instead want to check that the number is not 0. "
+ "If you gave a string, you may instead want to check that the string is not \"\".")];
......@@ -98,9 +99,9 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
/*
A list of valid transition names. Used by (transition:).
*/
const validT8ns = ["instant", "dissolve", "fade", "rumble", "shudder", "pulse", "zoom", "flicker", "slideleft", "slideright", "slideup", "slidedown",
"fadeleft", "faderight", "fadeup", "fadedown"];
const validBorders = ['dotted','dashed','solid','double','groove','ridge', 'inset','outset','none'];
const validT8ns = insensitiveSet("instant", "dissolve", "fade", "rumble", "shudder", "pulse", "zoom", "flicker", "slideleft", "slideright", "slideup", "slidedown",
"fadeleft", "faderight", "fadeup", "fadedown", "blur");
const validBorders = insensitiveSet('dotted','dashed','solid','double','groove','ridge', 'inset','outset','none');
Macros.addChanger
/*d:
(if: Boolean) -> Changer
......@@ -668,6 +669,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
* "fade-down" (causes the hook to gently fade in while sliding downward)
* "pulse" (causes the hook to instantly appear while pulsating rapidly)
* "zoom" (causes the hook to scale up from the mouse pointer)
* "blur" (causes the hook to appear from a blur)
All transitions are 0.8 seconds long, unless a (transition-time:) changer is added
to the changer.
......@@ -679,8 +681,10 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
the passage transitions used by links or (link-goto:), and trying to do so will cause an error. Please use
(transition-depart:) or (transition-arrive:) for this purpose instead.
The "blur" transition will not work in Internet Explorer 10 or 11.
See also:
(text-style:), (transition-time:), (transition-delay:), (transition-skip:)
(text-style:), (transition-time:), (transition-delay:), (transition-skip:), (animate:)
Added in: 1.0.0
#transitions 1
......@@ -697,7 +701,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
}
return d;
},
[insensitiveSet(...validT8ns)]
[validT8ns]
)
/*d:
......@@ -728,11 +732,9 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
d.transitionTime = time;
/*
(transition-time:) does a sort of unfortunate double duty: specifying the transition time
for hooks AND links. This is bearable because the likelihood of a link needing its own timed
transition and a differently-timed passage transition should be low (and can be worked around
using wrapper hooks anyway).
for hooks AND links.
*/
d.data.passageT8n = Object.assign(d.data.passageT8n || {}, { time });
d.data.passageT8n = assign(d.data.passageT8n || {}, { time });
return d;
},
[positiveNumber]
......@@ -841,12 +843,15 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
* "fade-down" (causes the passage to gently fade out while sliding downward)
* "pulse" (causes the passage to disappear while pulsating rapidly)
* "zoom" (causes the passage to shrink down toward the mouse pointer)
* "blur" (causes the passage to vanish into a blur)
Attaching this macro to a hook that isn't a passage link won't do anything (no error message will be produced).
You can't combine transitions by adding them together, like you can with (text-style:) -
`(t8n-depart:"dissolve")+(t8n-depart:"shudder")` won't make a transition that simultaneously dissolve-fades and shudders.
The "blur" transition will not work in Internet Explorer 10 or 11.
See also:
(transition-arrive:)
......@@ -856,7 +861,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
(["transition-depart", "t8n-depart"],
(_, name) => ChangerCommand.create("transition-depart", [Utils.insensitiveName(name)]),
(d, name) => {
d.data.passageT8n = Object.assign(d.data.passageT8n || {}, { depart:name });
d.data.passageT8n = assign(d.data.passageT8n || {}, { depart:name });
if (name === "zoom") {
d.data.passageT8n.departOrigin = function() {
const el = $(this), {left, top} = el.offset();
......@@ -865,7 +870,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
}
return d;
},
[insensitiveSet(...validT8ns)]
[validT8ns]
)
/*d:
......@@ -898,12 +903,15 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
* "fade-down" (causes the passage to gently fade in while sliding downward)
* "pulse" (causes the passage to disappear while pulsating rapidly)
* "zoom" (causes the passage to scale up from the mouse pointer)
* "blur" (causes the passage to appear from a blur)
Attaching this macro to a hook that isn't a passage link won't do anything (no error message will be produced).
You can't combine transitions by adding them together, like you can with (text-style:) -
`(t8n-depart:"dissolve")+(t8n-depart:"shudder")` won't make a transition that simultaneously dissolve-fades and shudders.
The "blur" transition will not work in Internet Explorer 10 or 11.
See also:
(transition-depart:)
......@@ -913,7 +921,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
(["transition-arrive", "t8n-arrive"],
(_, name) => ChangerCommand.create("transition-arrive", [Utils.insensitiveName(name)]),
(d, name) => {
d.data.passageT8n = Object.assign(d.data.passageT8n || {}, { arrive:name });
d.data.passageT8n = assign(d.data.passageT8n || {}, { arrive:name });
if (name === "zoom") {
d.data.passageT8n.arriveOrigin = function() {
const el = $(this), {left, top} = el.offset(), height = el.height();
......@@ -926,7 +934,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
}
return d;
},
[insensitiveSet(...validT8ns)]
[validT8ns]
)
/*d:
......@@ -1066,7 +1074,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
});
return d;
},
[insensitiveSet(...validBorders), ...Array(3).fill(optional(insensitiveSet(...validBorders)))]
[validBorders, ...Array(3).fill(optional(validBorders))]
)
/*d:
......@@ -1299,7 +1307,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
to convert to a percentage.)
*/
const alignPercent = Math.round(centerIndex / (arrow.length - 2) * 50);
style = Object.assign({
style = assign({
'text-align' : 'center',
'max-width' : '50%',
},
......@@ -1802,7 +1810,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
These map style names, as input by the author as this macro's first argument,
to CSS attributes that implement the styles. These are all hand-coded.
*/
styleTagNames = Object.assign(Object.create(null), {
styleTagNames = assign(Object.create(null), {
none: {},
bold: { 'font-weight': 'bold' },
italic: { 'font-style': 'italic' },
......@@ -2168,6 +2176,78 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
zeroOrMore(Everything)
);
/*
This is NOT a changer, but a command, yet, since it uses validT8ns, it is defined in this file extraordinarily.
*/
/*d:
(animate: HookName, String, [Number]) -> Command
A command that causes a hook to briefly animate, as if a (transition:) was applied to it. The length of time that the animation plays can be optionally
altered by providing a number.
Example usage:
`(when: time > 15s)[You'd better get going now, pardner. (animate:?passage's links, "rumble")]` causes all of the links in the passage to briefly
shake using the "rumble" transition after 15 seconds have passed.
Rationale:
Transitions allow incoming text to animate in a visually stylish fashion, but there are times you might want already displayed text to suddenly animate,
as if it had just transitioned in anew. This command can be useful, when used sparingly, to draw the attention of the player toward a particular part
of the passage, such as a link, or an easily missed word, after they click a link or wait a certain amount of time. It can be particularly interesting
when used to draw attention to a part that, until then, had nothing visually remarkable about it, so as to highlight it for only a moment.
Details:
(animate:) recognises the same transition names as (transition:), except for "instant" (which obviously cannot be animated). These names are:
* "dissolve" or "fade" (causes the hook to gently fade in)
* "flicker" (causes the hook to roughly flicker in - don't use with a long (transition-time:))
* "shudder" (causes the hook to instantly appear while shaking back and forth)
* "rumble" (causes the hook to instantly appear while shaking up and down)
* "slide-right" (causes the hook to slide in from left to right)
* "slide-left" (causes the hook to slide in from right to left)
* "slide-up" (causes the hook to slide in from bottom to top)
* "slide-down" (causes the hook to slide in from top to bottom)
* "fade-right" (causes the hook to gently fade in while sliding rightward)
* "fade-left" (causes the hook to gently fade in while sliding leftward)
* "fade-up" (causes the hook to gently fade in while sliding upward)
* "fade-down" (causes the hook to gently fade in while sliding downward)
* "pulse" (causes the hook to instantly appear while pulsating rapidly)
* "zoom" (causes the hook to scale up from the mouse pointer)
* "blur" (causes the hook to appear from a blur)
The optional time value, which alters the animation's length of time, corresponds to (transition-time:). Additional alterations to the animation
can be given by attaching the other two transition changers, (transition-delay:) and (transition-skip:), to this command.
You may notice that other, permanent animations are available as (text-style:) options. (animate:)'s animations operate separately to those,
and animations unique to (text-style:) can't be temporarily applied with this macro. Instead, use (change:) with (text-style:) to apply those
animations.
See also:
(show:), (rerun:), (transition:)
Added in: 3.2.0
#transitions
*/
Macros.addCommand
("animate",
$.noop,
(cd, section, hook, transition, transitionTime) => {
hook.forEach(section, target => {
/*
TBW
*/
let transitionOrigin;
if (name === "zoom") {
const {left, top} = target.offset();
transitionOrigin = (Utils.mouseCoords.x - left) + "px " + (Utils.mouseCoords.y - top) + "px";
}
Utils.transitionIn(target, transition, cd.transitionTime || transitionTime, cd.transitionDelay, cd.transitionSkip, 0,
transitionOrigin
);
});
},
[rest(HookSet), insensitiveSet(...validT8ns.innerType.filter(e => e !== "instant")), optional(positiveNumber)]);
/*d:
(box: String, [Number]) -> Changer
......@@ -2308,7 +2388,7 @@ define(['jquery','macros', 'utils', 'utils/renderutils', 'datatypes/colour', 'da
styles.height = height + (name === "box" ? "em" : "vh");
}
if (name === "float-box") {
Object.assign(styles, {
assign(styles, {
position: 'fixed',
top: top + "vh",
/*
......
......@@ -129,6 +129,7 @@
"fade-left": rev => rev ? "fade-right" : "fade-left",
"fade-up": rev => rev ? "fade-down" : "fade-up",
"fade-down": rev => rev ? "fade-up" : "fade-down",
blur: () => "blur",
};
const builtinColourNames = {
......@@ -160,7 +161,7 @@
["#212529","#c92a2a","#a61e4d","#862e9c","#5f3dc4","#364fc7","#1864ab","#0b7285","#087f5b","#2b8a3e","#5c940d","#e67700","#d9480f"]
];
const t8nNames = ["default", "", "instant", "dissolve", "rumble", "shudder", "pulse", "zoom", "flicker", "slide-left", "slide-right", "slide-up", "slide-down",
const t8nNames = ["default", "", "instant", "dissolve", "blur", "rumble", "shudder", "pulse", "zoom", "flicker", "slide-left", "slide-right", "slide-up", "slide-down",
"fade-left", "fade-right", "fade-up", "fade-down"];
function toCSSColour(colour, alpha) {
......
......@@ -404,7 +404,7 @@ define(['jquery', 'markup', 'utils/polyfills'],
as an inline attribute, well, that makes life easy for us.
*/
if (elem.hidden || /none|inline/.test(elem.style.display)
|| /display: (?!none|inline)/.test(elem.getAttribute('style'))) {
|| /display: (none|inline)/.test(elem.getAttribute('style'))) {
return true;
}
/*
......@@ -413,11 +413,10 @@ define(['jquery', 'markup', 'utils/polyfills'],
*/
if (usuallyBlockElements.includes(elem.tagName.toLowerCase())
/*
If it has an inline style which is NOT none or inline,
If it has an attribute style which is verifiably block (NOT none, inline, unset or inherit),
then go ahead and return false.
*/
|| /none|inline/.test(elem.style.display)
|| /display: (?!none|inline)/.test(elem.getAttribute('style'))) {
|| /display: (?!none|inline|inherit|unset)/.test(elem.getAttribute('style'))) {
return false;
}
/*
......@@ -433,7 +432,11 @@ define(['jquery', 'markup', 'utils/polyfills'],
unknown.push(elem);
return true;
})
&& unknown.every(elem => /none|inline/.test(elem.style.display));
/*
Since each element passed the "usually block" test, we can assume that a "" display property means it's
probably inline.
*/
&& unknown.every(elem => /none|inline|^$/.test(elem.style.display));
},
/*
......
......@@ -136,3 +136,8 @@
92%, to { opacity: 1 }
}
@include keyframes(blur) {
0% { filter:blur(2rem); opacity:0; }
25% { opacity:1; }
to { filter:blur(0rem); opacity:1; }
}
......@@ -49,13 +49,6 @@ $time: 0.8s;
}
}
/* Box-flash */
[data-t8n^=boxflash] {
&.transition-in {
@include animation(box-flash $time);
}
}
/* Pulse */
[data-t8n^=pulse] {
&.transition-in {
......@@ -80,6 +73,18 @@ $time: 0.8s;
}
}
/* Blur */
[data-t8n^=blur] {
&.transition-in {
@include animation(blur $time);
display:inline-block !important;
}
&.transition-out {
@include animation(blur $time reverse);
display:inline-block !important;
}
}
/* Slide */
/*
These can't have the hyphen in their data-t8n name because Utils.insensitiveName() removes it.
......
......@@ -238,7 +238,7 @@ describe("style changer macros", function() {
expect("(print:("+name+":'garply corge'))").markupToError();
["dissolve", "fade", "shudder", "pulse", "flicker", "instant", "rumble",
"slide-right", "slide-left", "slide-up", "slide-down", "pulse", "zoom",
"fade-right", "fade-left", "fade-up", "fade-down"].forEach(function(e) {
"fade-right", "fade-left", "fade-up", "fade-down", "blur"].forEach(function(e) {
expect("(print:("+name+":'" + e + "'))").not.markupToError();
});
});
......@@ -422,6 +422,18 @@ describe("style changer macros", function() {
it("is aliased to (t8n-"+name+":)", function() {
expect("(print: (t8n-"+name+":2s) is (transition-"+name+":2s))").markupToPrint("true");
});
if (name === "time") {
it("changes the animation-duration of the transition", function() {
var p = runPassage("(transition-"+name+":2s)+(t8n:'pulse')[foo]");
expect($('tw-transition-container[data-t8n="pulse"]').css('animation-duration')).toMatch(/^2000ms$|^2s$/);
});
}
if (name === "delay") {
it("changes the animation-delay of the transition", function() {
var p = runPassage("(transition-"+name+":2s)+(t8n:'pulse')[foo]");
expect($('tw-transition-container[data-t8n="pulse"]').css('animation-delay')).toMatch(/^2000ms$|^2s$/);
});
}
// TODO: Add .css() tests of output, including passage links.
});
});
......
......@@ -574,4 +574,28 @@ describe("basic command macros", function() {
expect('(history:)').markupToPrint('qux,qux,qux,grault,test');
});
});
describe("the (animate:) macro", function() {
it("takes a hook name, a transition name string that isn't 'instant', and an optional number", function() {
expect("(animate:)").markupToError();
expect("(animate:2)").markupToError();
expect("(animate:?A)").markupToError();
expect("(animate:?A,'Good')").markupToError();
expect("(animate:?A,'instant')").markupToError();
["dissolve", "fade", "shudder", "pulse", "flicker", "rumble",
"slide-right", "slide-left", "slide-up", "slide-down", "pulse", "zoom",
"fade-right", "fade-left", "fade-up", "fade-down", "blur"].forEach(function(e) {
expect("(animate:?A,'" + e + "')").not.markupToError();
});
expect("(animate:?A,'fade-left',2s)").not.markupToError();
expect("(animate:?A,'fade-left',0)").markupToError();
});
it("animates the named hooks with the given animation", function() {
var p = runPassage("|a>[foo]|b>[|a>[foo]](animate:?A,'pulse',9s)");
expect(p.find('tw-transition-container[data-t8n="pulse"] > tw-hook[name="a"]').length).toBe(2);
});
it("changes the animation-duration with the optional number", function() {
var p = runPassage("|a>[foo](animate:?A,'pulse',9s)");
expect(p.find('tw-transition-container[data-t8n="pulse"]').css('animation-duration')).toMatch(/^9000ms$|^9s$/);
});
});
});
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment