Animal
6 Feb 2009, 3:26 AM
Ever seen the MS Word feature where, if you select some text by double click or by dragging, a faintly opaque "Font Style" menu appears above the selection which becomes fully opaque if you mouse over it, but fades if you mouse away?
Here's a class (which may also be used as a plugin to fade an already shown Component) which does that.
It manages the opacity of one Component, but has two ways of triggering the fade. If you configure it with a trigger element, it uses proximity to that to fade in/out the Component.
If you do not specify a trigger, it uses proximity to the Component itself and it's up to you to show the component. The code below illustrates usage. Thanks to MJLecomte BTW, for making me think about this. I'm trying to think of a useful way of applying this to showing the grid filtering Panel in a smooth and unobtrusive way.
Drop the code below into examples/simple-widgets. It's a version of the qtips example.
The closable QuickTip has a ProximityFader as a plugin. It fades away as you move the mouse away from it. When you get to 100 pixels from any border, it hides.
Ext.override(Ext.lib.Region, {
/**
* Returns the shortest distance between this Region and another Region.
* Either or both Regions may be Points.
* @param {Region} r The other Region
* @return {Number} The shortest distance in pixels between the two Regions.
*/
getDistanceBetween: function(r) {
// We may need to mutate r, so make a copy.
r = Ext.apply({}, r);
// Translate r to the left of this
if (r.left > this.right) {
var rWidth = r.right - r.left;
r.left = this.left - (r.left - this.right) - rWidth;
r.right = r.left + rWidth;
}
// Translate r above this
if (r.top > this.bottom) {
var rHeight = r.bottom - r.top;
r.top = this.top - (r.top - this.bottom) - rHeight;
r.bottom = r.top + rHeight;
}
// If r is directly above
if (r.right > this.left) {
return this.top - r.bottom;
}
// If r is directly to the left
if (r.bottom > this.top) {
return this.left - r.right;
}
// r is on a diagonal path
return Math.round(Math.sqrt(Math.pow(this.top - r.bottom, 2) + Math.pow(this.left - r.right, 2)));
}
});
Ext.override(Ext.Element, {
/**
* Returns shortest distance between this Element and the specified point
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @return {Number} The shortest distance in pixels between this Element and the specified point.
*/
getDistanceTo: function(x, y) {
return this.getRegion().getDistanceBetween(new Ext.lib.Point(x, y));
},
/**
* Returns the shortest distance between this Element and another Element.
* @param {Element/DOMElement/String} el The other Element, or its ID.
* @return {Number} The shortest distance in pixels between the two Elements.
*/
getDistanceBetween: function(el) {
return this.getRegion().getDistanceBetween(Ext.fly(el).getRegion());
}
});
/**
* @class Ext.ux.ProximityFader
* Manages visibility of a Component based on the proximity of the mouse to a configured trigger Element:<pre><code>
new Ext.ux.ProximityFader({
threshold: 100, // When within 100 pixels of
trigger: proximityTriggerEl, // this Element,
component: myFloatingPanel // Begin fading in this Component.
});
*/
Ext.ux.ProximityFader = Ext.extend(Object, {
constructor: function(config) {
Ext.apply(this, config);
if (this.component) {
this.init(this.component);
}
},
init: function(component) {
this.component = component;
if (component.rendered) {
this.onComponentRender(component);
} else {
component.on({
render: this.onComponentRender,
single: true,
scope: this,
delay: 1
});
}
// If we have been configured with a trigger, always listen for proximity
if (this.trigger) {
Ext.getDoc().on('mousemove', this.onMouseMove, this);
} else {
// Otherwise the trigger is the Component's Element. Only listen while it's visible
component.on({
show: this.onShow,
hide: this.onHide,
scope: this
});
}
},
onMouseMove: function(e) {
var o = 1, d = this.el.getDistanceTo.apply(this.trigger, e.getXY());
if (d > this.threshold) {
this.component.hide();
} else if (d > 0) {
// Mouse is within range of the trigger, so show the Component if its not already visible
if (this.trigger && !this.component.isVisible()) {
this.component.show();
}
var o = 1 - (d / this.threshold);
}
this.el.setOpacity(o);
if (this.shadow) {
this.shadow.setOpacity(o);
}
},
onComponentRender: function(c) {
if (!this.trigger) {
this.trigger = c.el;
}
this.el = c.el;
if (this.el.shadow) {
this.shadow = this.el.shadow.el;
}
},
onShow: function() {
Ext.getDoc().on('mousemove', this.onMouseMove, this);
},
onHide: function() {
Ext.getDoc().un('mousemove', this.onMouseMove, this);
}
});
Ext.onReady(function(){
new Ext.ToolTip({
target: 'tip1',
html: 'A very simple tooltip'
});
new Ext.ToolTip({
target: 'ajax-tip',
width: 200,
autoLoad: {url: 'ajax-tip.html'},
dismissDelay: 15000 // auto hide after 15 seconds
});
new Ext.ToolTip({
target: 'tip2',
html: 'Click the X to close me',
title: 'My Tip Title',
autoHide: false,
closable: true,
draggable: true,
plugins: new Ext.ux.ProximityFader({
threshold: 100
}),
});
new Ext.ToolTip({
target: 'track-tip',
title: 'Mouse Track',
width:200,
html: 'This tip will follow the mouse while it is over the element',
trackMouse:true
});
Ext.QuickTips.init();
});
Here's a class (which may also be used as a plugin to fade an already shown Component) which does that.
It manages the opacity of one Component, but has two ways of triggering the fade. If you configure it with a trigger element, it uses proximity to that to fade in/out the Component.
If you do not specify a trigger, it uses proximity to the Component itself and it's up to you to show the component. The code below illustrates usage. Thanks to MJLecomte BTW, for making me think about this. I'm trying to think of a useful way of applying this to showing the grid filtering Panel in a smooth and unobtrusive way.
Drop the code below into examples/simple-widgets. It's a version of the qtips example.
The closable QuickTip has a ProximityFader as a plugin. It fades away as you move the mouse away from it. When you get to 100 pixels from any border, it hides.
Ext.override(Ext.lib.Region, {
/**
* Returns the shortest distance between this Region and another Region.
* Either or both Regions may be Points.
* @param {Region} r The other Region
* @return {Number} The shortest distance in pixels between the two Regions.
*/
getDistanceBetween: function(r) {
// We may need to mutate r, so make a copy.
r = Ext.apply({}, r);
// Translate r to the left of this
if (r.left > this.right) {
var rWidth = r.right - r.left;
r.left = this.left - (r.left - this.right) - rWidth;
r.right = r.left + rWidth;
}
// Translate r above this
if (r.top > this.bottom) {
var rHeight = r.bottom - r.top;
r.top = this.top - (r.top - this.bottom) - rHeight;
r.bottom = r.top + rHeight;
}
// If r is directly above
if (r.right > this.left) {
return this.top - r.bottom;
}
// If r is directly to the left
if (r.bottom > this.top) {
return this.left - r.right;
}
// r is on a diagonal path
return Math.round(Math.sqrt(Math.pow(this.top - r.bottom, 2) + Math.pow(this.left - r.right, 2)));
}
});
Ext.override(Ext.Element, {
/**
* Returns shortest distance between this Element and the specified point
* @param {Number} x The x coordinate.
* @param {Number} y The y coordinate.
* @return {Number} The shortest distance in pixels between this Element and the specified point.
*/
getDistanceTo: function(x, y) {
return this.getRegion().getDistanceBetween(new Ext.lib.Point(x, y));
},
/**
* Returns the shortest distance between this Element and another Element.
* @param {Element/DOMElement/String} el The other Element, or its ID.
* @return {Number} The shortest distance in pixels between the two Elements.
*/
getDistanceBetween: function(el) {
return this.getRegion().getDistanceBetween(Ext.fly(el).getRegion());
}
});
/**
* @class Ext.ux.ProximityFader
* Manages visibility of a Component based on the proximity of the mouse to a configured trigger Element:<pre><code>
new Ext.ux.ProximityFader({
threshold: 100, // When within 100 pixels of
trigger: proximityTriggerEl, // this Element,
component: myFloatingPanel // Begin fading in this Component.
});
*/
Ext.ux.ProximityFader = Ext.extend(Object, {
constructor: function(config) {
Ext.apply(this, config);
if (this.component) {
this.init(this.component);
}
},
init: function(component) {
this.component = component;
if (component.rendered) {
this.onComponentRender(component);
} else {
component.on({
render: this.onComponentRender,
single: true,
scope: this,
delay: 1
});
}
// If we have been configured with a trigger, always listen for proximity
if (this.trigger) {
Ext.getDoc().on('mousemove', this.onMouseMove, this);
} else {
// Otherwise the trigger is the Component's Element. Only listen while it's visible
component.on({
show: this.onShow,
hide: this.onHide,
scope: this
});
}
},
onMouseMove: function(e) {
var o = 1, d = this.el.getDistanceTo.apply(this.trigger, e.getXY());
if (d > this.threshold) {
this.component.hide();
} else if (d > 0) {
// Mouse is within range of the trigger, so show the Component if its not already visible
if (this.trigger && !this.component.isVisible()) {
this.component.show();
}
var o = 1 - (d / this.threshold);
}
this.el.setOpacity(o);
if (this.shadow) {
this.shadow.setOpacity(o);
}
},
onComponentRender: function(c) {
if (!this.trigger) {
this.trigger = c.el;
}
this.el = c.el;
if (this.el.shadow) {
this.shadow = this.el.shadow.el;
}
},
onShow: function() {
Ext.getDoc().on('mousemove', this.onMouseMove, this);
},
onHide: function() {
Ext.getDoc().un('mousemove', this.onMouseMove, this);
}
});
Ext.onReady(function(){
new Ext.ToolTip({
target: 'tip1',
html: 'A very simple tooltip'
});
new Ext.ToolTip({
target: 'ajax-tip',
width: 200,
autoLoad: {url: 'ajax-tip.html'},
dismissDelay: 15000 // auto hide after 15 seconds
});
new Ext.ToolTip({
target: 'tip2',
html: 'Click the X to close me',
title: 'My Tip Title',
autoHide: false,
closable: true,
draggable: true,
plugins: new Ext.ux.ProximityFader({
threshold: 100
}),
});
new Ext.ToolTip({
target: 'track-tip',
title: 'Mouse Track',
width:200,
html: 'This tip will follow the mouse while it is over the element',
trackMouse:true
});
Ext.QuickTips.init();
});