143 lines
4.3 KiB
JavaScript
143 lines
4.3 KiB
JavaScript
import ol_interaction_Clip from './Clip.js'
|
|
import ol_coordinate_cspline from '../render/Cspline.js'
|
|
|
|
/** Blob interaction to clip layers in a blob
|
|
* @constructor
|
|
* @extends {ol_interaction_Clip}
|
|
* @param {*} options blob options
|
|
* @param {number} options.radius radius of the clip, default 100
|
|
* @param {ol.layer|Array<ol.layer>} options.layers layers to clip
|
|
* @param {number} [options.stiffness=20] spring stiffness coef, default 20
|
|
* @param {number} [options.damping=7] spring damping coef
|
|
* @param {number} [options.mass=1] blob mass
|
|
* @param {number} [options.points=10] number of points for the blob polygon
|
|
* @param {number} [options.tension=.5] blob polygon spline tension
|
|
* @param {number} [options.fuss] bob fussing factor
|
|
* @param {number} [options.amplitude=1] blob deformation amplitude factor
|
|
*/
|
|
var ol_interaction_Blob = class olinteractionBlob extends ol_interaction_Clip {
|
|
constructor(options) {
|
|
super(options);
|
|
}
|
|
/** Animate the blob
|
|
* @private
|
|
*/
|
|
precompose_(e) {
|
|
if (!this.getActive())
|
|
return;
|
|
var ctx = e.context;
|
|
var ratio = e.frameState.pixelRatio;
|
|
|
|
ctx.save();
|
|
if (!this.pos) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(0, 0);
|
|
ctx.clip();
|
|
return;
|
|
}
|
|
|
|
var pt = [this.pos[0], this.pos[1]];
|
|
var tr = e.inversePixelTransform;
|
|
if (tr) {
|
|
pt = [
|
|
(pt[0] * tr[0] - pt[1] * tr[1] + tr[4]),
|
|
(-pt[0] * tr[2] + pt[1] * tr[3] + tr[5])
|
|
];
|
|
} else {
|
|
pt[0] *= ratio;
|
|
pt[1] *= ratio;
|
|
}
|
|
|
|
// Time laps
|
|
if (!this.frame)
|
|
this.frame = e.frameState.time;
|
|
var dt = e.frameState.time - this.frame;
|
|
this.frame = e.frameState.time;
|
|
// Blob position
|
|
pt = this._getCenter(pt, dt);
|
|
// Blob geom
|
|
var blob = this._calculate(dt);
|
|
// Draw
|
|
var p = blob[0];
|
|
ctx.beginPath();
|
|
ctx.moveTo(pt[0] + p[0], pt[1] + p[1]);
|
|
for (var i = 1; p = blob[i]; i++) {
|
|
ctx.lineTo(pt[0] + p[0], pt[1] + p[1]);
|
|
}
|
|
ctx.clip();
|
|
e.frameState.animate = true;
|
|
}
|
|
/** Get blob center with kinetic
|
|
* @param {number} dt0 time laps
|
|
* @private
|
|
*/
|
|
_getCenter(pt, dt0) {
|
|
if (!this._center) {
|
|
this._center = pt;
|
|
this._velocity = [0, 0];
|
|
} else {
|
|
var k = this.get('stiffness') || 20; // stiffness
|
|
var d = -1 * (this.get('damping') || 7); // damping
|
|
var mass = Math.max(this.get('mass') || 1, .1);
|
|
var dt = Math.min(dt0 / 1000, 1 / 30);
|
|
|
|
var fSpring = [
|
|
k * (pt[0] - this._center[0]),
|
|
k * (pt[1] - this._center[1])
|
|
];
|
|
var fDamping = [
|
|
d * this._velocity[0],
|
|
d * this._velocity[1]
|
|
];
|
|
|
|
var accel = [
|
|
(fSpring[0] + fDamping[0]) / mass,
|
|
(fSpring[1] + fDamping[1]) / mass
|
|
];
|
|
|
|
this._velocity[0] += accel[0] * dt;
|
|
this._velocity[1] += accel[1] * dt;
|
|
|
|
this._center[0] += this._velocity[0] * dt;
|
|
this._center[1] += this._velocity[1] * dt;
|
|
}
|
|
return this._center;
|
|
}
|
|
/** Calculate the blob geom
|
|
* @param {number} dt time laps
|
|
* @returns {Array<ol_coordinate>}
|
|
* @private
|
|
*/
|
|
_calculate(dt) {
|
|
var i, nb = this.get('points') || 10;
|
|
if (!this._waves || this._waves.length !== nb) {
|
|
this._waves = [];
|
|
for (i = 0; i < nb; i++) {
|
|
this._waves.push({
|
|
angle: Math.random() * Math.PI,
|
|
noise: Math.random()
|
|
});
|
|
}
|
|
}
|
|
var blob = [];
|
|
var speed = (this._velocity[0] * this._velocity[0] + this._velocity[1] * this._velocity[1]) / 500;
|
|
this._rotation = (this._rotation || 0) + (this._velocity[0] > 0 ? 1 : -1) * Math.min(.015, speed / 70000 * dt);
|
|
for (i = 0; i < nb; i++) {
|
|
var angle = i * 2 * Math.PI / nb + this._rotation;
|
|
var radius = this.radius + Math.min(this.radius, speed);
|
|
var delta = Math.cos(this._waves[i].angle) * radius / 4 * this._waves[i].noise * (this.get('amplitude') || 1);
|
|
blob.push([
|
|
(this.radius + delta) * Math.cos(angle),
|
|
(this.radius + delta) * Math.sin(angle)
|
|
]);
|
|
// Add noise
|
|
this._waves[i].angle += (Math.PI + Math.random() + speed / 200) / 350 * dt * (this.get('fuss') || 1);
|
|
this._waves[i].noise = Math.min(1, Math.max(0, this._waves[i].noise + (Math.random() - .5) * .1 * (this.get('fuss') || 1)));
|
|
}
|
|
blob.push(blob[0]);
|
|
return ol_coordinate_cspline(blob, { tension: this.get('tension') });
|
|
}
|
|
}
|
|
|
|
export default ol_interaction_Blob
|