Difference between revisions of "Pie menu"

From Organic Design
m (Feb 8 finishing touches)
(Change source-code blocks to standard format)
 
(18 intermediate revisions by 2 users not shown)
Line 35: Line 35:
  
 
All the changes made to convert the links from a standard bullet list to a star menu are done in JavaScript/jQuery, all the PHP does is wrap the bullet list in a class so that the JavaScript knows which bullet lists to convert. Here's the code which operates directly on the link elements composing the bullet list and re-positions them into a circle.
 
All the changes made to convert the links from a standard bullet list to a star menu are done in JavaScript/jQuery, all the PHP does is wrap the bullet list in a class so that the JavaScript knows which bullet lists to convert. Here's the code which operates directly on the link elements composing the bullet list and re-positions them into a circle.
{{code|<js>$( function() {
+
<source lang="js">
 +
$( function() {
 
$('div.tam-star').each( function() { // apply the following to all star menus
 
$('div.tam-star').each( function() { // apply the following to all star menus
 
$('a', this).css('position','absolute') // apply the following to all <a> elements in this menu
 
$('a', this).css('position','absolute') // apply the following to all <a> elements in this menu
Line 50: Line 51:
 
});
 
});
 
});
 
});
});</js>}}
+
});
 +
</source>
  
 
=== Feb 1 - Recursive positioning ===
 
=== Feb 1 - Recursive positioning ===
Line 59: Line 61:
 
|&nbsp;&nbsp;&nbsp;&nbsp;
 
|&nbsp;&nbsp;&nbsp;&nbsp;
 
|
 
|
{{code|<js>// Get the depth of this element
+
<source lang="js">
 +
// Get the depth of this element
 
var li = e.parent();
 
var li = e.parent();
 
var d = 0;
 
var d = 0;
Line 72: Line 75:
 
var ox = parent.position().left + parent.width() / 2;
 
var ox = parent.position().left + parent.width() / 2;
 
var oy = parent.position().top + parent.height() / 2;
 
var oy = parent.position().top + parent.height() / 2;
}</js>}}
+
}
 +
</source>
 
|}
 
|}
  
Line 80: Line 84:
 
[[File:Star-progress-3.jpg|800px]]
 
[[File:Star-progress-3.jpg|800px]]
  
{{code|<js>// Create a unique ID and persistent data for this element
+
<source lang="js">
 +
// Create a unique ID and persistent data for this element
 
e.attr('id', 'starnode' + window.stars.length);
 
e.attr('id', 'starnode' + window.stars.length);
 
window.stars.push( { parent: p, depth: d } );
 
window.stars.push( { parent: p, depth: d } );
Line 100: Line 105:
 
e.css('left', ox + x - e.width() / 2).css('top', oy + y - e.height() / 2);
 
e.css('left', ox + x - e.width() / 2).css('top', oy + y - e.height() / 2);
 
}
 
}
});</js>}}
+
});
 +
</source>
  
 
=== Feb 6 - Interaction ===
 
=== Feb 6 - Interaction ===
Line 252: Line 258:
 
|&nbsp;&nbsp;&nbsp;&nbsp;
 
|&nbsp;&nbsp;&nbsp;&nbsp;
 
|
 
|
{{code|<js>e.click( function() {
+
<source lang="js">
 +
e.click( function() {
 
$(this).animate( { t: 100 }, {
 
$(this).animate( { t: 100 }, {
 
duration: 500,
 
duration: 500,
Line 276: Line 283:
 
}
 
}
 
});
 
});
});</js>}}
+
});
 +
</source>
 
|}
 
|}
  
Line 283: Line 291:
 
<html>
 
<html>
 
<div class="tam-star">
 
<div class="tam-star">
<ul><li><a rel="nofollow" class="external text" href="http://118.localhost/A">A</a>
+
<ul><li><a href="/wiki/index.php?title=1&amp;action=edit&amp;redlink=1" class="new" title="1 (page does not exist)">1</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Aa">Aa</a>
+
</li><li><a href="/wiki/index.php?title=2&amp;action=edit&amp;redlink=1" class="new" title="2 (page does not exist)">2</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Bar">Bar</a>
+
<ul><li><a href="/wiki/index.php?title=V&amp;action=edit&amp;redlink=1" class="new" title="V (page does not exist)">v</a>
<ul><li><a rel="nofollow" class="external text" href="http://118.localhost/A">A</a>
+
</li><li><a href="/wiki/index.php?title=Vi&amp;action=edit&amp;redlink=1" class="new" title="Vi (page does not exist)">vi</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/B">B</a>
+
</li><li><a href="/wiki/index.php?title=Vii&amp;action=edit&amp;redlink=1" class="new" title="Vii (page does not exist)">vii</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Biz">Biz</a>
+
</li></ul>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/C">C</a>
+
</li><li><a href="/wiki/index.php?title=3&amp;action=edit&amp;redlink=1" class="new" title="3 (page does not exist)">3</a>
<ul><li><a rel="nofollow" class="external text" href="http://118.localhost/1">1</a>
+
</li><li><a href="/wiki/index.php?title=4&amp;action=edit&amp;redlink=1" class="new" title="4 (page does not exist)">4</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/2">2</a>
+
 
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/3">3</a>
+
<ul><li><a href="/wiki/index.php?title=Viii&amp;action=edit&amp;redlink=1" class="new" title="Viii (page does not exist)">viii</a>
 +
</li><li><a href="/wiki/index.php?title=Ix&amp;action=edit&amp;redlink=1" class="new" title="Ix (page does not exist)">ix</a>
 +
</li><li><a href="/X" title="X">x</a>
 +
</li><li><a href="/wiki/index.php?title=Xi&amp;action=edit&amp;redlink=1" class="new" title="Xi (page does not exist)">xi</a>
 +
</li></ul>
 +
</li><li><a href="/wiki/index.php?title=5&amp;action=edit&amp;redlink=1" class="new" title="5 (page does not exist)">5</a>
 +
</li></ul>
 +
</li><li><a href="/wiki/index.php?title=B&amp;action=edit&amp;redlink=1" class="new" title="B (page does not exist)">B</a>
 +
</li><li><a href="/wiki/index.php?title=C&amp;action=edit&amp;redlink=1" class="new" title="C (page does not exist)">C</a>
 +
<ul><li><a href="/wiki/index.php?title=6&amp;action=edit&amp;redlink=1" class="new" title="6 (page does not exist)">6</a>
 +
 
 +
</li><li><a href="/wiki/index.php?title=7&amp;action=edit&amp;redlink=1" class="new" title="7 (page does not exist)">7</a>
 +
<ul><li><a href="/I" title="I" class="mw-redirect">i</a>
 +
</li><li><a href="/wiki/index.php?title=Ii&amp;action=edit&amp;redlink=1" class="new" title="Ii (page does not exist)">ii</a>
 +
</li><li><a href="/wiki/index.php?title=Iii&amp;action=edit&amp;redlink=1" class="new" title="Iii (page does not exist)">iii</a>
 +
</li><li><a href="/wiki/index.php?title=Iv&amp;action=edit&amp;redlink=1" class="new" title="Iv (page does not exist)">iv</a>
 
</li></ul>
 
</li></ul>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/D">D</a>
+
</li><li><a href="/wiki/index.php?title=8&amp;action=edit&amp;redlink=1" class="new" title="8 (page does not exist)">8</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/E">E</a>
 
 
</li></ul>
 
</li></ul>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Baz">Baz</a>
+
</li><li><a href="/wiki/index.php?title=D&amp;action=edit&amp;redlink=1" class="new" title="D (page does not exist)">D</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Foo">Foo</a>
+
</li><li><a href="/E" title="E">E</a>
</li><li><a rel="nofollow" class="external text" href="http://118.localhost/Hello">Hello</a>
+
 
 +
</li><li><a href="/wiki/index.php?title=F&amp;action=edit&amp;redlink=1" class="new" title="F (page does not exist)">F</a>
 +
</li><li><a href="/wiki/index.php?title=G&amp;action=edit&amp;redlink=1" class="new" title="G (page does not exist)">G</a>
 
</li></ul>
 
</li></ul>
 
</div>
 
</div>
<script>
+
<script>/*<![CDATA[*/
 +
window.tamBaseUrl = '';
 
window.star_config = {
 
window.star_config = {
img_node: '/img/star-node-sml-gold.png',
+
img_node: '/files/d/d7/Star-node-4.png',
img_leaf: '/img/star-node-sml.png',
+
img_leaf: '/files/5/5b/Star-node-4b.png',
img_disabled: '/img/star-node-sml-gold.png',
 
 
radii: [120,90,40,20],
 
radii: [120,90,40,20],
 
duration: 500,
 
duration: 500,
Line 405: Line 429:
 
var display = 'block';
 
var display = 'block';
 
var o = t * window.star_config.out_spin;
 
var o = t * window.star_config.out_spin;
 +
var d = data.depth;
  
 
// Set origin for the children to this elements center
 
// Set origin for the children to this elements center
Line 419: Line 444:
 
t = 1 - t;
 
t = 1 - t;
 
}
 
}
 +
 +
// If opening, check siblings to see if one needs to be closed
 +
else if( d > 1 ) { if( fx.pos == 0 ) {
 +
var pdata = getData(data.parent);
 +
for( var i in pdata.children ) {
 +
var sibling = pdata.children[i];
 +
var sdata = getData(sibling);
 +
if( sdata.open ) animateNode(sibling);
 +
}
 +
} }
  
 
// Current radius for this elements children
 
// Current radius for this elements children
var d = data.depth;
 
 
var r = window.star_config.radii;
 
var r = window.star_config.radii;
 
r = d > r.length ? r[r.length-1] : r[d-1];
 
r = d > r.length ? r[r.length-1] : r[d-1];
Line 441: Line 475:
 
 
 
// If closing, and this is the first iteration, close this child too if open
 
// If closing, and this is the first iteration, close this child too if open
if( data.open && fx.pos == 0 && cdata.open ) animateNode(child);
+
if( data.open ) if( fx.pos == 0 ) if( cdata.open ) animateNode(child);
 
}
 
}
 
},
 
},
Line 459: Line 493:
 
return window.stars[e.attr('id').substr(8)];
 
return window.stars[e.attr('id').substr(8)];
 
}
 
}
</script>
+
/*]]>*/</script>
 
</html>
 
</html>
  
 +
==Bug==
  
[[Category:Projects]]
+
<s>Click on root, then bar, then baz, then root.  Bar will not collapse. It seems that if a non-expanded node clicked on, it "trips" the focus from baz so that baz doesn't collapse with the rest of the leafs. </s>
 +
::refreshed page, can't duplicate.
 +
:::Are you doing that testing right here? or did you install TreeAndMenu etc? if here - you were probably testing on the star-menu above under Feb 6<sup>th</sup>. It wasn't until the Feb 8<sup>th</sup> session that the closing code finalised to close all opened child nodes.
 +
[[Category:Old Projects]]

Latest revision as of 18:11, 22 May 2015

User story

As a user, I want to easily navigate to key points in an article so that I can see what other concepts are linked to in an article, and also see what key points (children) are related to the key points in the current article (parent). I also want to simply see what key points (grandchildren) are linked to children articles.

Example

http://www.woweb.ru/js/12/082/

Elaboration

  • Given an article foo with [[ ]]links to articles foobar, bar and barfoo i want to see a concept menu that centers on foo (current article) and when i click on (+) next to foo, it will expand out dynamic indexed all hyperlinks in given article in an equidistant format.
  • (+) will expand out nodes, instead of being indicated by color
  • starburst or "concept map"
  • initially, only 2 levels deep: parent (current article), child (links in article), grandchild (links in children article)
  • if article bar contains links to a and b, then when i expand the foo node out to bar, and click on a (+) sign for bar, I will see a and b as the terminal (leaf) node.
  • Dynamic: If I add a link to the article foo, then the "pie menu" will be autimatically updated
  • call it pie because that is what is is in sims. also, will need to use pi to calculate the distribution of nodes
  • with a large amount of links, may need to do a lighbox kinda thing and fade out bacground when the map is activated, and new nodes may be larger than parent nodes, and may need to fade out / overlap parent nodes.

Ideas

  • different color of nodes?
  • Base it on category instead of links
  • Base it on headers instead of links
  • n nodes, set terminus (leaf) node
  • use with TreeAndMenu syntax

Progress reports

Jan 30 - Link extraction

Ok the first easy bit has been done which is the LinkTree extension that extracts the links out from the page to the requested depth. It adds a #linktree parser function which takes one parameter for the depth (defaults to 1). Here's a screenshot of how it's used, the last example shows how to use it with TreeAndMenu which will soon have a new #star function added.

LinkTreeExample.jpg

Jan 31 - Star menu started

I've made a start on the complex stuff now, here's a screenshot of the results after the first session of adding the #star parser-function to the TreeAndMenu extension: Star-progress-1.jpg

All the changes made to convert the links from a standard bullet list to a star menu are done in JavaScript/jQuery, all the PHP does is wrap the bullet list in a class so that the JavaScript knows which bullet lists to convert. Here's the code which operates directly on the link elements composing the bullet list and re-positions them into a circle.

$( function() {
	$('div.tam-star').each( function() { // apply the following to all star menus
		$('a', this).css('position','absolute') // apply the following to all <a> elements in this menu
			    .css('left', $(this).position().left + r)
			    .css('top', $(this).position().top + r)
			    .each( function() {
				var e = $(this);
				var i = e.parent().index(); // the index of the <li> that this <a> is in
				var n = e.parent().parent().children().last().index() + 1; // the index of the last <li> + 1
				var a = Math.PI * 2 * i / n;
				var y = Math.sin(a) * 100;
				var x = Math.cos(a) * 100;
				e.css('left', e.position().left+x-e.width()/2).css('top', e.position().top+y-e.height()/2);
		});
	});
});

Feb 1 - Recursive positioning

The difficult calculation of the recursive position was worked out today. Here's a screenshot of it calculating the position of nested items to three levels. The circles have been added for clarity. The depth of each <a> element was calculated by checking how many <li> elements it has hierarchically above it, then the centre position for this element is calculated from the position of the <a> in the parent <li>, or from the centre of the root <div> element if it's a top-level item (depth=1). The code to the right of the screenshot shows these calculations.


Star-progress-2.jpg
    
// Get the depth of this element
var li = e.parent();
var d = 0;
while( li[0].tagName == 'LI' ) {
	li = li.parent().parent();
	d++;
}

// Get the origin for this element from the parent <a> or <div>
var parent = e.parent().parent().parent();
if( d > 1 ) parent = parent.children().first();
var ox = parent.position().left + parent.width() / 2;
var oy = parent.position().top + parent.height() / 2;
}

Feb 2 - Animation

In today's session on the star menu I focussed on getting the basics of the animation working using jQuery's animate() method. Currently I've just applied the animation in a general way so that every <a> element simply animates from it's parent's centre (which is also moving) to its designated position on the circumference around its parent. I had to create a global array of all the <a> elements so that data could be associated with them and retrieved from within the animation callback. Here's some screenshots from the animation sequence, followed by the global data and animation code.

Star-progress-3.jpg

// Create a unique ID and persistent data for this element
e.attr('id', 'starnode' + window.stars.length);
window.stars.push( { parent: p, depth: d } );

// Animate the element from the parent to the circumference
var r = 120;
e.animate( { radius: r }, { // just using a dummy property to set the range for "now"
	duration: 1000,
	step: function( now, fx ) {
		var e = $(fx.elem);
		var p = window.stars[ e.attr('id').substr(8) ].parent; // get parent element from the global array
		var ox = p.position().left + p.width() / 2;
		var oy = p.position().top + p.height() / 2;
		var i = e.parent().index();
		var n = e.parent().parent().children().last().index() + 1;
		var a = Math.PI * 2 * i / n;
		var y = Math.sin(a) * now; // radius changed to the animating "now" parameter
		var x = Math.cos(a) * now;
		e.css('left', ox + x - e.width() / 2).css('top', oy + y - e.height() / 2);
	}
});

Feb 6 - Interaction

Today was the big one which has 99% completed the project. It involved adding the click event to the nodes so that they can animate open or closed depending on their current state. The example below is an active version of the code after today's session which opens opens out to three levels (click Root, then Bar, then C). A snippet of the mouse click event and the associated animation code are shown to the right of the example. There's still a few things required to finalise the project though, such as making the items link to their targets properly, allowing only the outer-most open nodes to be clickable, and using different icons to show which are disabled or contain no children.


    
e.click( function() {
	$(this).animate( { t: 100 }, {
		duration: 500,
		easing: 'swing',
		step: function(now, fx) {
			var t = fx.pos;
			var e = $(fx.elem);
			var data = window.stars[e.attr('id').substr(8)];
			var k = Math.PI * 2 / data.children.length;
			for( var i in data.children ) {
				var child = data.children[i];

				...

			}
		},

		// Toggle the status on completion
		complete: function() {
			var e = $(this);
			var data = window.stars[e.attr('id').substr(8)];
			data.open = !data.open;
		}
	});
});

Feb 8 finishing touches

Today in the hotel at Foz do Iguaçu I got the finishing touches done. The whole structure closes if its open to a deep level and then a higher level such as the root node is clicked. The links work properly (clicking on the word follows the link, but clicking on the node icon manipulates the menu). And different icons are used to indicate whether or not the node contains any child nodes that can be opened. All the characteristics such as animation speed, the icons used, whether and how much the animation rotates when opening/closing, and the radius for each depth are all adjustable from a configuration array.

  • B
  • C
  • D
  • E
  • F
  • G
  • Bug

    Click on root, then bar, then baz, then root. Bar will not collapse. It seems that if a non-expanded node clicked on, it "trips" the focus from baz so that baz doesn't collapse with the rest of the leafs.

    refreshed page, can't duplicate.
    Are you doing that testing right here? or did you install TreeAndMenu etc? if here - you were probably testing on the star-menu above under Feb 6th. It wasn't until the Feb 8th session that the closing code finalised to close all opened child nodes.