<div id="raphael"></div>
<p id="copyright">photo by <a href="https://www.flickr.com/photos/jar0d/" target="_blank">Sander van der Wel</a></p>
@import "compass/css3";

#raphael{
  height: 550px;
  width: 410px;
  margin: 0 auto;
}

#copyright{
  position: fixed;
  z-index: 10000;
  bottom: 30px;
  right: 10px;
  font-size: 11px;
  color: #333;
  font-family: 'MS Pゴシック', 'MS PGothic', 'ヒラギノ角ゴ ProN W3', 'Hiragino Kaku Gothic ProN', メイリオ, Meiryo, sans-serif;
  
  a{
    color: #333;
    
    &:hover{
      color: #8ad5d8;
      text-decoration: underline;
    }
  }
}
View Compiled
(function($){
	$(function(){
		var r = Raphael('raphael', '100%', '100%');
		var img = r.image('https://techapi.manulneko.com/files/2014/04/4519098234_f43f010be1_z.jpg', 0, 0, 640, 640);
		
		var mask = document.createElementNS('http://www.w3.org/2000/svg', 'clipPath');
		mask.setAttribute('id', 'mask');
		
		var path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
		mask.appendChild(path);
		r.defs.appendChild(mask);
	
		img.node.setAttribute('clip-path', 'url(#mask)');
	
		
		var Circle = function(x, y, rr){
			//this.path = r.path().attr({stroke: '#666', 'stroke-width': 5});
	
			var h = rr/2;
			this.x = x;
			this.y = y;
	
			this.curve = {
				top: [x+h, y, x+rr, y+h, x+rr, y+rr],
				right: [x+rr, y+rr+h, x+h, y+rr*2, x, y+rr*2],
				bottom: [x-h, y+rr*2, x-rr, y+rr+h, x-rr, y+rr],
				left: [x-rr, y+h, x-h, y, x, y]
			};
	
			this.draw();
		};
	
		Circle.prototype.draw = function(){
			var pp = [];
			for(var i in this.curve){
				pp = pp.concat(this.curve[i]);
			}
	
			var p = [
				['M', this.x, this.y],
				['C', pp],
				['Z']
			];
	
			//this.path.attr({path: p});
		
			var pathStr = '';
			for(var i in p){
				pathStr += p[i].join(',');
			}
			pathStr = pathStr.replace('M,', 'M').replace('C,', 'C');
	
			path.setAttribute('d', pathStr);
		};
	
		Circle.prototype.updateTop = function(x, y){
			this.x += x;
			this.y += y;
	
			for(var i = 0; i < 2; i++){
				this.curve.top[i+i*1] += x;
				this.curve.top[i+i*1+1] += y;
			}
	
			for(var i = 1; i < 3; i++){
				this.curve.left[i+i*1] += x;
				this.curve.left[i+i*1+1] += y;
			}
	
			this.draw();
		};
	
		Circle.prototype.updateRight = function(x, y){
			for(var i = 0; i < 2; i++){
				this.curve.right[i+i*1] += x;
				this.curve.right[i+i*1+1] += y;
			}
	
			for(var i = 1; i < 3; i++){
				this.curve.top[i+i*1] += x;
				this.curve.top[i+i*1+1] += y;
			}
	
			this.draw();
		};
	
		Circle.prototype.updateBottom = function(x, y){
			for(var i = 0; i < 2; i++){
				this.curve.bottom[i+i*1] += x;
				this.curve.bottom[i+i*1+1] += y;
			}
	
			for(var i = 1; i < 3; i++){
				this.curve.right[i+i*1] += x;
				this.curve.right[i+i*1+1] += y;
			}
	
			this.draw();
		};
	
		Circle.prototype.updateLeft = function(x, y){
			for(var i = 0; i < 2; i++){
				this.curve.left[i+i*1] += x;
				this.curve.left[i+i*1+1] += y;
			}
	
			for(var i = 1; i < 3; i++){
				this.curve.bottom[i+i*1] += x;
				this.curve.bottom[i+i*1+1] += y;
			}
	
			this.draw();
		};
	
		var c = new Circle(200, 100, 180);
	
	
		var sinCurve = function(){
			this.phase = 0;
			this.speed = Math.random()*10+5;
			this.amp = 1;
		};
	
		sinCurve.prototype.update = function(){
			this.phase += this.speed;
			return Math.sin(this.phase*Math.PI/180)*this.amp;
		};
	
		var ss = [];
		for(var i = 0; i < 4; i++){
			ss[i] = new sinCurve();
		}
	
		setInterval(function(){
			c.updateTop(0, ss[0].update());
			c.updateRight(ss[1].update(), 0);
			c.updateBottom(0, ss[2].update());
			c.updateLeft(ss[3].update(), 0);
		}, 50);
	});
})(jQuery);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js
  2. //cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js