Skip to content

Instantly share code, notes, and snippets.

@edom18
Created March 4, 2013 00:27
Show Gist options
  • Save edom18/5079073 to your computer and use it in GitHub Desktop.
Save edom18/5079073 to your computer and use it in GitHub Desktop.
おっぱいがうごくJS inspire by damele0n
[damele0n](http://jsdo.it/damele0n)さんの[おっぱいがうごくJS](http://jsdo.it/damele0n/h8BW)を参考に、新しく作ってみた。
(あ、案件で使うときがあったので・・w)
主な実装内容は、胸のあたりに5x5程度のメッシュを作成し、テクスチャマッピングした各頂点を揺らす、ということで実装しています。
[History]
* [2013/03/04] 常にレンダリングし続けるのを防ぎ、CPU使用率を下げるよう修正しました。
body {
background: #111;
}
div, p {
margin: 0;
padding: 0;
}
#container {
position: absolute;
left: 50px;
top: 50px;
}
#cv,
#cv2 {
cursor: pointer;
position: absolute;
z-index: 10;
}
.char {
position: absolute;
left: 0;
top: 0;
}
<p><input type="button" id="btn" value="Debug mode" /></p>
<div id="container">
<canvas id="cv"></canvas>
<canvas id="cv2"></canvas>
<p class="char"><img src="http://jsrun.it/assets/w/y/x/d/wyxdz.jpg" alt="" /></p>
<!-- /#container --></div>
<script>
document.addEventListener('DOMContentLoaded', function () {
var btn = document.getElementById('btn');
var canvasElem = document.getElementById('cv');
var canvasElem2 = document.getElementById('cv2');
var src = 'http://jsrun.it/assets/w/y/x/d/wyxdz.jpg';
var oppai = new Oppai({
cv: canvasElem,
src: src,
mesh: 6,
rect: [215, 364, 330, 518],
elasticity: 0.82,
responsiveness: 0.25,
displacementRadius: 75,
displacementIntensity: -0.15
});
var oppai2 = new Oppai({
cv: canvasElem2,
src: src,
mesh: 6,
rect: [320, 364, 430, 518],
elasticity: 0.82,
responsiveness: 0.25,
displacementRadius: 75,
displacementIntensity: -0.15
});
btn.addEventListener('click', function () {
oppai.debug = !oppai.debug;
oppai2.debug = !oppai2.debug;
}, false);
});
</script>
do (win = window, doc = window.document, exports = window) ->
{sqrt, atan2, max, cos, sin, abs, ceil} = Math
ARC = Math.PI * 2
requestAnimFrame = do ->
return window.requestAnimationFrame or
window.webkitRequestAnimationFrame or
window.mozRequestAnimationFrame or
window.oRequestAnimationFrame or
window.msRequestAnimationFrame or
( callback, element ) ->
window.setTimeout(callback, 1000 / 60)
class EventHandler
handleEvent: (evt) ->
@[evt.type]?(evt);
# -----------------------------------------------------------------------
class Oppai extends EventHandler
mesh: 10
constructor: (settings) ->
@[s] = settings[s] for s of settings
@center =
x: ~~((@rect[2] - @rect[0]) / 2)
y: ~~((@rect[3] - @rect[1]) / 2)
@cursors = {}
@g = @cv.getContext '2d'
@_img = new Image()
@_img.onload = @onload
@_img.src = @src
@left = @rect[0]
@top = @rect[1]
@width = @rect[2] - @left
@height = @rect[3] - @top
@offsetX = @cv.parentNode.offsetLeft + @left
@offsetY = @cv.parentNode.offsetTop + @top
@cv.style.left = "#{@left}px"
@cv.style.top = "#{@top}px"
@cv.addEventListener 'mousedown', @, false
onload: =>
@img = doc.createElement 'canvas'
@width = @img.width = @cv.width = @width
@height = @img.height = @cv.height = @height
ctx = @img.getContext '2d'
ctx.drawImage @_img, @rect[0], @rect[1], @width, @height, 0, 0, @width, @height
@createVertex()
@play()
drawTriangles: (vertecies, indecies, uvData) ->
cv = doc.createElement 'canvas'
_g = cv.getContext '2d'
g = @g
img = @img
w = cv.width = img.width
h = cv.height = img.height
for index, i in indecies by 3
i0 = indecies[i + 0]
i1 = indecies[i + 1]
i2 = indecies[i + 2]
v0 = vertecies[i0]
v1 = vertecies[i1]
v2 = vertecies[i2]
uv0 = uvData[i0]
uv1 = uvData[i1]
uv2 = uvData[i2]
v0x = v0.x; v0y = v0.y
v1x = v1.x; v1y = v1.y
v2x = v2.x; v2y = v2.y
uv0x = uv0.x; uv0y = uv0.y
uv1x = uv1.x; uv1y = uv1.y
uv2x = uv2.x; uv2y = uv2.y
_Ax = v1x - v0x; _Ay = v1y - v0y
_Bx = v2x - v0x; _By = v2y - v0y
Ax = (uv1x - uv0x) * w; Ay = (uv1y - uv0y) * h
Bx = (uv2x - uv0x) * w; By = (uv2y - uv0y) * h
m = new M22
m._11 = Ax; m._12 = Ay
m._21 = Bx; m._22 = By
#To invert
mi = m.getInvert()
if mi is null then return
a = mi._11 * _Ax + mi._12 * _Bx
c = mi._21 * _Ax + mi._22 * _Bx
b = mi._11 * _Ay + mi._12 * _By
d = mi._21 * _Ay + mi._22 * _By
tx = v0x - (a * uv0x * w + c * uv0y * h)
ty = v0y - (b * uv0x * w + d * uv0y * h)
_g.save()
_g.beginPath()
_g.moveTo v0x, v0y
_g.lineTo v1x, v1y
_g.lineTo v2x, v2y
#for debugging
if @debug is true
_g.strokeStyle = '#444'
_g.stroke()
_g.clip()
_g.closePath()
_g.setTransform a, b, c, d, tx, ty
_g.drawImage img, 0, 0
_g.restore()
g.clearRect 0, 0, w, h
g.drawImage cv, 0, 0
createVertex: ->
mesh = @mesh
img = @img
mm = new MeshMgr @, @g, img, mesh
w = img.width
h = img.height
wi = w / mesh
hi = h / mesh
_x = 0
_y = 0
len = (mesh * mesh) + (mesh * 2) + 1
for i in [0...len]
ignore = false
x = wi * _x
y = hi * _y
if x >= w
_x = 0
_y++
corner = true
else
_x++
corner = false
if y >= h
corner = true
dx = @center.x - x
dy = @center.y - y
distance = sqrt(dx * dx + dy * dy)
ignore = true if distance > @displacementRadius
ux = x / w
uy = y / h
mm.addVertex
x: x
y: y
ux: ux
uy: uy
corner: corner
ignore: ignore
seg = mesh + 1
meshes = mm.getMesh()
for m, i in meshes when m.corner isnt true
i1 = (i + 0)
i2 = (i + 1)
i3 = (i + seg + 0)
i4 = (i + seg + 1)
mm.addIndex(i1, i2, i3)
mm.addIndex(i4, i3, i2)
@mm = mm
draw: ->
g = @g
mm = @mm
vertecies = mm.getVertecies()
indecies = mm.getIndecies()
uvData = mm.getUvData()
chk = vertecies.length
for i in [0...vertecies.length]
vertecx = vertecies[i]
vertecx.update()
if vertecx.stopped is true or vertecx.ignore is true
chk--
#if all vertecies are stopped, skip rendering.
if chk is 0
return
g.clearRect 0, 0, @width, @height
@drawTriangles vertecies, indecies, uvData
play: ->
requestAnimFrame =>
@draw()
@play()
mousedown: (event) ->
@cursorStart event
event.preventDefault()
mousemove: (event) ->
@cursors.mouse = event
mouseup: (event) ->
@cursorEnd event
cursorStart: (cursor) ->
@cursors.mouse = cursor
doc.addEventListener 'mousemove', @, false
doc.addEventListener 'mouseup', @, false
cursorEnd: (event) ->
delete @cursors.mouse
doc.removeEventListener 'mousemove', @, false
doc.removeEventListener 'mouseup', @, false
# ------------------------------------------------------------------
class M22
constructor: ->
@_11 = 1; @_12 = 0
@_21 = 0; @_22 = 1
getInvert: ->
out = new M22
det = @_11 * @_22 - @_12 * @_21
if 0.0001 > det > -0.0001
return null
out._11 = @_22 / det
out._22 = @_11 / det
out._12 = @_12 / det
out._21 = @_21 / det
return out
# ------------------------------------------------------------------
class MeshMgr
constructor: (@parent, @g, @img, @mesh) ->
@vertecies = []
@indecies = []
@uvData = []
addVertex: (attributes) ->
attributes.parent = @parent
@vertecies.push(new Vertex attributes)
@uvData.push(new UvData attributes.ux, attributes.uy)
addIndex: (i1, i2, i3) ->
@indecies.push(i1, i2, i3);
getVertecies: ->
@vertecies
getIndecies: ->
@indecies
getUvData: ->
@uvData
getMesh: ->
@vertecies
# ------------------------------------------------------------------
class Vertex
constructor: (attributes) ->
for id of attributes
@[id] = attributes[id]
@ox = @x
@oy = @y
@angle = @offsetX = @offsetY = 0
elasticizeProperty: (prop, target) ->
deltaProp = "#{prop}Delta"
@[deltaProp] = (@[deltaProp] or 0) * @parent.elasticity + (@[prop] - target) * @parent.responsiveness
if abs(@[deltaProp]) < 0.001
@stopped = true
else
@stopped = false
@[prop] -= @[deltaProp]
update: ->
return if @ignore
if 'mouse' of @parent.cursors
cursors = @parent.cursors.mouse
dx = cursors.pageX - @x - @parent.offsetX
dy = cursors.pageY - @y - @parent.offsetY
distance = sqrt(dx * dx + dy * dy)
angle = atan2(dy, dx)
@angle = angle or @angle
targetD = if distance then @parent.displacementRadius - distance else 0
targetD = max(targetD, 0) * @parent.displacementIntensity
targetOffsetX = cos(@angle) * -targetD
targetOffsetY = sin(@angle) * -targetD
@elasticizeProperty 'offsetX', targetOffsetX
@elasticizeProperty 'offsetY', targetOffsetY
@x = @ox + @offsetX
@y = @oy + @offsetY
# ------------------------------------------------------------------
class UvData
constructor: (@x, @y) ->
# ------------------------------------------------------------------
# EXPORTS
# ------------------------------------------------------------------
exports.Oppai = Oppai
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment