二级菜单优化功能

二级菜单的功能优化---渐入佳境

功能1:点击其中之一的一级菜单展示其二级菜单,同时其他一级菜单的二级菜单都处于闭合状态

思路:使用jsDOM操作进行实现

HTML示例代码
<div class="multi-menu">
    {% for menu in menu_list %}
        <div class="item">
            <div class="title"><i class="fa {{ menu.icon }}"></i>
                {{ menu.name }}</div>
            <div class="body">
                {% for child in menu.children %}
                    <a href="{{ child.url }}">{{ child.title }}</a>
                {% endfor %}
                
            </div>
        </div>
    {% endfor %}
</div>

js代码实现

$('.item .title').click(function () {
    $(this).next().removeClass('hide').parent().siblings().find('.body').addClass('hide')
})

//1.先命中一级菜单title,触发点击事件
//2.保证自己没有被隐藏
//3.找到其他body使其隐藏,通过自己先找到父级parent,再找到所有的兄弟siblings的儿子,也就是body,命中后添加hide属性

//如此便实现了这个只显示当前菜单下的二级菜单功能

功能2:访问当前页面,默认选中对应页面展示菜单

思路:在访问的二级菜单url对应a标签中添加 class='active'

1.django中 templatetags文件下使用inclusion_tag进行自定义模板

#从二级菜单的数据结构中获取到需要使用的url,title进行模板渲染
"""
{
    1: {
        'name': '信息管理',
        'icon': 'fa-email',
        'children': [{
            'title': '客户管理',
            'url': '/customer/list/'
        }]
    },
}
"""
from django import template
import re
from permission import settings
register = template.Library()
#自定义模板
@register.inclusion_tag('menu.html')
def menu(request):
    menu_dic = request.session.get(settings.MENU_SESSION_KEY)
    url = request.path_info #获取访问的url

    #获取到url和title进行模板渲染
    for item in menu_dic.values(): #首先循环拿到item表示一级菜单id对应的所有内容

        for i in item['children']: #循环item['children'] 要拿到i 表示二级菜单内的所有内容
            if re.match("^{}$".format(i['url']),url): #将获取到url与session中的url进行正则匹配
                i['class'] = 'active' #匹配成功后给二级菜单字典内添加一组键值对 class='active'
                break
    return {'menu_list':menu_dic.values()}

 

2.在对应的模板中直接渲染即可,参照功能1中的HTML代码

  <a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a>

根据上面的HTML代码来看,在class=body 下的a标签中添加class='active'进行渲染即可.
***如此便实现了
访问某个页面,默认选中对应页面的展示菜单***

 

功能3:访问某个页面,页面对应菜单的二级菜单展示,其他菜单的二级菜单默认关闭

思路:首先先隐藏所有的二级菜单,再根据访问的url展示对应的一级菜单下的二级菜单

#示例代码
from
django import template import re from luffy_permission import settings register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY) url = request.path_info for item in menu_dic.values(): item['class'] = 'hide' #将所有的二级菜单隐藏 for i in item['children']: if re.match("^{}$".format(i['url']),url): i['class'] = 'active' item['class'] = '' #将匹配成功的二级菜单取消隐藏 break return {'menu_list':menu_dic.values()}

 

<div class="multi-menu">
    {% for menu in menu_list %}
        <div class="item">
            <div class="title"><i class="fa {{ menu.icon }}"></i>
                {{ menu.name }}</div>
            <div class="body {{ menu.class }}">    
                {% for child in menu.children %}
                    <a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a>
                {% endfor %}
                
            </div>
        </div>
    {% endfor %}
</div>

注:先在body中添加class='hide'属性,令所有的二级菜单隐藏,当访问的url页面匹配与session中的url匹配成功后在将 class='' 从而取消隐藏

 

功能4:固定菜单栏展示顺序,因为数据结构是字典(无序的)

思路:将menu表结构改变,添加一个字段weight = model.IntegerField(default=1),导入有序字典(from collections import OrderedDict),根据数值大小进行排序即可

models.py
class
Menu(models.Model): """ 一级菜单 """ name = models.CharField(max_length=30,verbose_name='一级菜单名称') icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) weight = models.IntegerField(default=1) def __str__(self): return self.name #添加一个字段weight

 

rbac.py
from django import template import re from permission import settings from collections import OrderedDict register = template.Library() #自定义模板 @register.inclusion_tag('menu.html') def menu(request): menu_dic = request.session.get(settings.MENU_SESSION_KEY)
   url = request.path_info order_dic
= OrderedDict() #实例化一个有序字典 for key in sorted(menu_dic,key=lambda n:menu_dic[n]['weight'],reverse=True): order_dic[key] = item = menu_dic[key] #将根据weight字段排序后的menu_dic 赋值给order_dic item['class'] = 'hide' for i in item['children']: if re.match("^{}$".format(i['url']),url): i['class'] = 'active' item['class'] = '' break return {'menu_list':order_dic.values()} #再返回有序字典在模板上顺序渲染

 

功能5:非菜单权限归属问题 (二级菜单内各种时操作 默认选中并处于显示对应二级菜单)

思路:二级菜单下需要有操作的层级关系,也就是你伪三级菜单 ,更改表结构也就是 权限表(Permission)

 步骤1:表结构设计 models.py

 

#参照上图所示
class
Permission(models.Model): """ 权限表 menu_id 有的话 表示当前的权限是二级菜单 父权限 没有 是一个普通权限 parent_id 有的话 表示当前就是子权限 没有的话就是一个父权限 """ url = models.CharField(max_length=255,verbose_name='地址') title = models.CharField(max_length=25,verbose_name='标题') menu = models.ForeignKey('Menu',blank=True,null=True) parent = models.ForeignKey('self') #自关联 获取到层级结构

 

 步骤2:查询和存放在session中的权限信息处理 service/permission.py

 1 from django.conf import settings
 2 
 3 
 4 def init_permission(request,obj):
 5     # 先查到需要的权限信息,
 6 
 7     permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
 8                                                                                      'permissions__title',
 9                                                                                      'permissions__menu__name',
10                                                                                      'permissions__menu__icon',
11                                                                                      'permissions__menu__id',
12                                                                                      'permissions__menu__weight',
13                                                                                      'permissions__id',
14                                                                                      'permissions__parent_id',
15                                                                                      ).distinct()
16     # 存放权限信息列表
17     permission_list = []
18     # 存放菜单信息的字典
19     menu_dic = {}
20     for item in permission_query:
21         # 将权限信息放到permission_list
22         permission_list.append({'url': item['permissions__url'],
23                                 'pid': item['permissions__parent_id'],
24                                 'id': item['permissions__id'] })
25 
26         #放入菜单信息
27         menu_id = item['permissions__menu__id']
28         if not menu_id:
29             continue
30         if menu_id not in menu_dic:
31             menu_dic[menu_id] = {
32                 'name': item['permissions__menu__name'],
33                 'icon': item['permissions__menu__icon'],
34                 'weight': item['permissions__menu__weight'],
35                 'children': [{
36                     'title': item['permissions__title'],
37                     'url': item['permissions__url'],
38                     'id': item['permissions__id'],
39                 }]
40             }
41         else:
42             menu_dic[menu_id]['children'].append({
43                 'title': item['permissions__title'],
44                 'url': item['permissions__url'],
45                 'id': item['permissions__id'],
46             })
47 
48     request.session[settings.PERMISSION_SESSION_KEY] = permission_list
49     request.session[settings.MENU_SESSION_KEY] = menu_dic
权限信息查询和session中保存

 

步骤3.中间件权限校验,处理访问url归属问题 middlewares/rbac.py

 1 from django.utils.deprecation import MiddlewareMixin
 2 import re
 3 from django.conf import settings
 4 from django.shortcuts import HttpResponse,redirect,reverse
 5 
 6 class RbacMiddleware(MiddlewareMixin):
 7 
 8     def process_request(self,request):
 9         #获取当前的url
10         url = request.path_info
11 
12         #白名单
13         for i in settings.WHITE_LIST:
14             if re.match(i,url):
15                 return
16 
17         #获取权限信息
18         permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
19         if not permission_list:
20             return redirect(reverse('login'))
21         #权限校验
22 
23         for item in permission_list:
24             # i 里面有{url pid  id}
25             if re.match("^{}$".format(item['url']),url):
26                 #要显示的二级菜单的id
27                 pid = item.get('pid')
28                 id = item.get('id')
29                 #此步操作就是表示不管是pid还是id都是显示的二级菜单的id
30                 if pid:
31                     #当前访问的是子权限,找父权限显示
32                     request.current_menu_id = pid
33                 else:
34                     #当前访问的是父权限,也就是二级菜单,找自己显示
35                     request.current_menu_id = id
36                 return
37         return HttpResponse('您没有访问的权限')
中间件处理访问的权限url归属问题

 

步骤4.自定义模板中二级菜单id和session中存的匹配成功

from django import template
import re
from luffy_permission import settings
from collections import OrderedDict

register = template.Library()
#自定义模板
@register.inclusion_tag('menu.html')
def menu(request):
    menu_dic = request.session.get(settings.MENU_SESSION_KEY)
    url = request.path_info  
    order_dic = OrderedDict()  
    for i in sorted(menu_dic,key=lambda n:menu_dic[n]['weight'],reverse=True):
        order_dic[i] = item = menu_dic[i] 
    # for item in order_dic.values(): 
        item['class'] = 'hide'  
        for i in item['children']: 
            if i['id'] == request.current_menu_id:#session中的id和访问的二级菜单id匹配
                i['class'] = 'active' 
                item['class'] = '' 
                break
    return {'menu_list':order_dic.values()}
自定义模板

 功能6:路径导航(breadcrumb)

思路:先写个字典,字典中添加要显示的url和title,然后进行循环,具体逻辑代码展现

步骤1:将存放在session的权限信息从列表修改为字典,同时在权限信息中再添加title字段,确保能够展示,

permission_dic[item['permissions__id']] = ({'url': item['permissions__url'],
                                                    'pid': item['permissions__parent_id'],
                                                    'id': item['permissions__id'] ,
                                                    'title': item['permissions__title'],
                                                    })

#确保url和title字段存在,能够访问跳转和显示

 

步骤2:中间件中 middlewares/rbac.py

  2-1添加一个列表套字典的数据结构并添加到request中

request.breadcrumb_list = [
            {'title':'首页','url':'/index/'}
        ]

 

 2-2 向breadcrumb_list中添加匹配到的pid/id对应的url和title主要是在访问的时候对应的将对二级菜单和三级菜单的url和标题展示
     for item in permission_dic.values():
            if re.match("^{}$".format(item['url']),url):
                pid = item.get('pid')
                id = item.get('id')
                #此步操作就是表示不管是pid还是id都是显示的二级菜单的id
                if pid:
                    #当前访问的是子权限,找父权限显示
                    request.current_menu_id = pid
                    #向路径导航列表中添加三级菜单的url和title
                    request.breadcrumb_list.append(
                        {'title': permission_dic[str(pid)]['title'],
                         'url': permission_dic[str(pid)]['url']
                         }
                    )
                    # 向路径导航列表中添加二级菜单的url和title
                    request.breadcrumb_list.append({'title': item['title'], 'url': item['url']})

                else:
                    #当前访问的是父权限,也就是二级菜单,找自己显示
                    request.current_menu_id = id
                    request.breadcrumb_list.append({'title':item['title'],'url':item['url']})

 

步骤3 templatetags/inclusion_tag进行自定义模板rabc.py

@register.inclusion_tag('breadcrumb.html')
def breadcrumb(request):
    return {'breadcrumb_list':request.breadcrumb_list}


#将获取到的request.breadcrumb_list 交给'breadcrumb.html'渲染

 

步骤4:HTML模板渲染

<div>
    <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
        {% for breadcrumb in breadcrumb_list %}
            {% if forloop.last %}
                <li>{{ breadcrumb.title }}</li>
            {% else %}
                <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a></li>
            {% endif %}

        {% endfor %}
    </ol>
</div>

#继承母版,单独写一个breadcrumb.html文件 #小小技术点:循环展示的时候,如果是最后一个无法访问,通过bootstrap 路径导航给li标签添加class='active'实现

 

功能7:权限控制到按钮级别

思路:根据用户是否有权限通过url反向解析的名称进行判断,有就通过过滤器显示,否则隐藏

步骤1:修改表结构,针对权限表(Permission)添加一个urlname字段,并将urls.py中url反向解析名称进行保存 例如:  name='customer_list'

#更新的表结构models.py
添加以下字段
urlname = models.CharField(max_length=32,verbose_name='url别名')

 

步骤2:获取到需要的权限信息 service/permission.py权限初始化

permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                                     'permissions__title',
                                                                                     'permissions__urlname',#取到权限表的url别名
                                                                                     'permissions__menu__name',
                                                                                     'permissions__menu__icon',
                                                                                     'permissions__menu__id',
                                                                                     'permissions__menu__weight',
                                                                                     'permissions__id',
                                                                                     'permissions__parent_id',
                                                                                     'permissions__parent__urlname', #取到父权限和自己的别名两个
                                                                                     ).distinct()
    # 存放权限信息列表
    permission_dic = {}
    # 存放菜单信息的字典
    menu_dic = {}
    for item in permission_query:
        # 将权限信息放到permission_list
        permission_dic[item['permissions__urlname']] = ({'url': item['permissions__url'],
                                                    'pid': item['permissions__parent_id'],
                                                    'pname': item['permissions__parent__urlname'],
                                                    'id': item['permissions__id'] ,
                                                    'urlname': item['permissions__urlname'] ,
                                                    'title': item['permissions__title'],
                                                    })

 

步骤3.中间件中进行权限校验,使用权限表中的id和自关联的parent_id字段,匹配出对应的url和title

        for item in permission_dic.values():
            if re.match("^{}$".format(item['url']),url):
                pid = item.get('pid')
                pname = item.get('pname')
                id = item.get('id')
                if pid:
                    request.current_menu_id = pid
                    #向路径导航列表中添加二级菜单也就是父权限的url和title
                    request.breadcrumb_list.append(
                        {'title': permission_dic[pname]['title'],
                         'url': permission_dic[pname]['url']
                         }
                    )
                    # 向路径导航列表中添加三级菜单的url和title
                    request.breadcrumb_list.append({'title': item['title'], 'url': item['url']})

                else:
                    #当前访问的是父权限,也就是二级菜单,找自己显示
                    request.current_menu_id = id
                    request.breadcrumb_list.append({'title':item['title'],'url':item['url']})#向路径导航中添加二级菜单
                return

 

步骤4.在templatetags/rbac.py中配置过滤器预备模板渲染data

@register.filter()
def has_permission(request,urlname):

    if urlname in request.session.get(settings.PERMISSION_SESSION_KEY):
        return True

#通过url反向解析的名称有无进行判断,从而在展示模板中进行显示

步骤5.HTML模板渲染出权限对应的操作信息

权限对应的操作

 

posted @ 2019-02-27 23:01  FindSoul  阅读(464)  评论(0编辑  收藏  举报
var RENDERER = { POINT_INTERVAL : 5, FISH_COUNT : 3, MAX_INTERVAL_COUNT : 50, INIT_HEIGHT_RATE : 0.5, THRESHOLD : 50, init : function(){ this.setParameters(); this.reconstructMethods(); this.setup(); this.bindEvent(); this.render(); }, setParameters : function(){ this.$window = $(window); this.$container = $('#jsi-flying-fish-container'); this.$canvas = $('
'); this.context = this.$canvas.appendTo(this.$container).get(0).getContext('2d-disabled'); this.points = []; this.fishes = []; this.watchIds = []; }, createSurfacePoints : function(){ var count = Math.round(this.width / this.POINT_INTERVAL); this.pointInterval = this.width / (count - 1); this.points.push(new SURFACE_POINT(this, 0)); for(var i = 1; i < count; i++){ var point = new SURFACE_POINT(this, i * this.pointInterval), previous = this.points[i - 1]; point.setPreviousPoint(previous); previous.setNextPoint(point); this.points.push(point); } }, reconstructMethods : function(){ this.watchWindowSize = this.watchWindowSize.bind(this); this.jdugeToStopResize = this.jdugeToStopResize.bind(this); this.startEpicenter = this.startEpicenter.bind(this); this.moveEpicenter = this.moveEpicenter.bind(this); this.reverseVertical = this.reverseVertical.bind(this); this.render = this.render.bind(this); }, setup : function(){ this.points.length = 0; this.fishes.length = 0; this.watchIds.length = 0; this.intervalCount = this.MAX_INTERVAL_COUNT; this.width = this.$container.width(); this.height = this.$container.height(); this.fishCount = this.FISH_COUNT * this.width / 500 * this.height / 500; this.$canvas.attr({width : this.width, height : this.height}); this.reverse = false; this.fishes.push(new FISH(this)); this.createSurfacePoints(); }, watchWindowSize : function(){ this.clearTimer(); this.tmpWidth = this.$window.width(); this.tmpHeight = this.$window.height(); this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL)); }, clearTimer : function(){ while(this.watchIds.length > 0){ clearTimeout(this.watchIds.pop()); } }, jdugeToStopResize : function(){ var width = this.$window.width(), height = this.$window.height(), stopped = (width == this.tmpWidth && height == this.tmpHeight); this.tmpWidth = width; this.tmpHeight = height; if(stopped){ this.setup(); } }, bindEvent : function(){ this.$window.on('resize', this.watchWindowSize); this.$container.on('mouseenter', this.startEpicenter); this.$container.on('mousemove', this.moveEpicenter); this.$container.on('click', this.reverseVertical); }, getAxis : function(event){ var offset = this.$container.offset(); return { x : event.clientX - offset.left + this.$window.scrollLeft(), y : event.clientY - offset.top + this.$window.scrollTop() }; }, startEpicenter : function(event){ this.axis = this.getAxis(event); }, moveEpicenter : function(event){ var axis = this.getAxis(event); if(!this.axis){ this.axis = axis; } this.generateEpicenter(axis.x, axis.y, axis.y - this.axis.y); this.axis = axis; }, generateEpicenter : function(x, y, velocity){ if(y < this.height / 2 - this.THRESHOLD || y > this.height / 2 + this.THRESHOLD){ return; } var index = Math.round(x / this.pointInterval); if(index < 0 || index >= this.points.length){ return; } this.points[index].interfere(y, velocity); }, reverseVertical : function(){ this.reverse = !this.reverse; for(var i = 0, count = this.fishes.length; i < count; i++){ this.fishes[i].reverseVertical(); } }, controlStatus : function(){ for(var i = 0, count = this.points.length; i < count; i++){ this.points[i].updateSelf(); } for(var i = 0, count = this.points.length; i < count; i++){ this.points[i].updateNeighbors(); } if(this.fishes.length < this.fishCount){ if(--this.intervalCount == 0){ this.intervalCount = this.MAX_INTERVAL_COUNT; this.fishes.push(new FISH(this)); } } }, render : function(){ requestAnimationFrame(this.render); this.controlStatus(); this.context.clearRect(0, 0, this.width, this.height); this.context.fillStyle = 'hsl(0, 0%, 95%)'; for(var i = 0, count = this.fishes.length; i < count; i++){ this.fishes[i].render(this.context); } this.context.save(); this.context.globalCompositeOperation = 'xor'; this.context.beginPath(); this.context.moveTo(0, this.reverse ? 0 : this.height); for(var i = 0, count = this.points.length; i < count; i++){ this.points[i].render(this.context); } this.context.lineTo(this.width, this.reverse ? 0 : this.height); this.context.closePath(); this.context.fill(); this.context.restore(); } }; var SURFACE_POINT = function(renderer, x){ this.renderer = renderer; this.x = x; this.init(); }; SURFACE_POINT.prototype = { SPRING_CONSTANT : 0.03, SPRING_FRICTION : 0.9, WAVE_SPREAD : 0.3, ACCELARATION_RATE : 0.01, init : function(){ this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE; this.height = this.initHeight; this.fy = 0; this.force = {previous : 0, next : 0}; }, setPreviousPoint : function(previous){ this.previous = previous; }, setNextPoint : function(next){ this.next = next; }, interfere : function(y, velocity){ this.fy = this.renderer.height * this.ACCELARATION_RATE * ((this.renderer.height - this.height - y) >= 0 ? -1 : 1) * Math.abs(velocity); }, updateSelf : function(){ this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height); this.fy *= this.SPRING_FRICTION; this.height += this.fy; }, updateNeighbors : function(){ if(this.previous){ this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height); } if(this.next){ this.force.next = this.WAVE_SPREAD * (this.height - this.next.height); } }, render : function(context){ if(this.previous){ this.previous.height += this.force.previous; this.previous.fy += this.force.previous; } if(this.next){ this.next.height += this.force.next; this.next.fy += this.force.next; } context.lineTo(this.x, this.renderer.height - this.height); } }; var FISH = function(renderer){ this.renderer = renderer; this.init(); }; FISH.prototype = { GRAVITY : 0.4, init : function(){ this.direction = Math.random() < 0.5; this.x = this.direction ? (this.renderer.width + this.renderer.THRESHOLD) : -this.renderer.THRESHOLD; this.previousY = this.y; this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1); if(this.renderer.reverse){ this.y = this.getRandomValue(this.renderer.height * 1 / 10, this.renderer.height * 4 / 10); this.vy = this.getRandomValue(2, 5); this.ay = this.getRandomValue(0.05, 0.2); }else{ this.y = this.getRandomValue(this.renderer.height * 6 / 10, this.renderer.height * 9 / 10); this.vy = this.getRandomValue(-5, -2); this.ay = this.getRandomValue(-0.2, -0.05); } this.isOut = false; this.theta = 0; this.phi = 0; }, getRandomValue : function(min, max){ return min + (max - min) * Math.random(); }, reverseVertical : function(){ this.isOut = !this.isOut; this.ay *= -1; }, controlStatus : function(context){ this.previousY = this.y; this.x += this.vx; this.y += this.vy; this.vy += this.ay; if(this.renderer.reverse){ if(this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE){ this.vy -= this.GRAVITY; this.isOut = true; }else{ if(this.isOut){ this.ay = this.getRandomValue(0.05, 0.2); } this.isOut = false; } }else{ if(this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE){ this.vy += this.GRAVITY; this.isOut = true; }else{ if(this.isOut){ this.ay = this.getRandomValue(-0.2, -0.05); } this.isOut = false; } } if(!this.isOut){ this.theta += Math.PI / 20; this.theta %= Math.PI * 2; this.phi += Math.PI / 30; this.phi %= Math.PI * 2; } this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY); if(this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD || this.vx < 0 && this.x < -this.renderer.THRESHOLD){ this.init(); } }, render : function(context){ context.save(); context.translate(this.x, this.y); context.rotate(Math.PI + Math.atan2(this.vy, this.vx)); context.scale(1, this.direction ? 1 : -1); context.beginPath(); context.moveTo(-30, 0); context.bezierCurveTo(-20, 15, 15, 10, 40, 0); context.bezierCurveTo(15, -10, -20, -15, -30, 0); context.fill(); context.save(); context.translate(40, 0); context.scale(0.9 + 0.2 * Math.sin(this.theta), 1); context.beginPath(); context.moveTo(0, 0); context.quadraticCurveTo(5, 10, 20, 8); context.quadraticCurveTo(12, 5, 10, 0); context.quadraticCurveTo(12, -5, 20, -8); context.quadraticCurveTo(5, -10, 0, 0); context.fill(); context.restore(); context.save(); context.translate(-3, 0); context.rotate((Math.PI / 3 + Math.PI / 10 * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1)); context.beginPath(); if(this.renderer.reverse){ context.moveTo(5, 0); context.bezierCurveTo(10, 10, 10, 30, 0, 40); context.bezierCurveTo(-12, 25, -8, 10, 0, 0); }else{ context.moveTo(-5, 0); context.bezierCurveTo(-10, -10, -10, -30, 0, -40); context.bezierCurveTo(12, -25, 8, -10, 0, 0); } context.closePath(); context.fill(); context.restore(); context.restore(); this.controlStatus(context); } }; $(function(){ RENDERER.init(); });