带有多个元素的 SVG 悬停
Posted
技术标签:
【中文标题】带有多个元素的 SVG 悬停【英文标题】:SVG hover with multiple elements 【发布时间】:2014-05-21 02:52:11 【问题描述】:我在两个 svg 元素上应用了 mouseover
/mouseout
事件。目标是将mouseover
上的掩码半径增加到由变量(maxMaskRadius
)指定的大小,并将mouseout
上的decrease
增加到初始状态(initialMaskRadius
)。
我只使用一个元素就可以完美地工作。但是当我有两个元素并从一个元素悬停到另一个元素时,前一个元素的动画会立即中止。但我想让它动画回到它的初始状态。不幸的是,使用我当前的代码是不可能的。
关于如何正确执行此操作的任何建议?
DEMO
CSS:
.dday.highlight .overlay
fill: rgba(247,99,62,0.8);
.dday.normal
width: 288px;
height: 288px;
HTML:
<svg class="dday highlight normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="car.jpg">
<image />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" style="mask: url(#mask1)" onmouseover="initAnimation(evt)" onmouseout="initAnimation(evt)" />
</a>
<mask id="mask1">
<rect x="0" y="0" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
<svg class="dday highlight normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="nokia.jpg">
<image />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" style="mask: url(#mask2)" onmouseover="initAnimation(evt)" onmouseout="initAnimation(evt)" />
</a>
<mask id="mask2">
<rect x="0" y="0" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
JS:
var maxImageWidth = 250,
maxImageHeight = 196,
ease = 50,
speed = 12,
maxMaskRadius = 100,
svg = null,
svgWidth = null,
svgHeight = null,
mask = null,
maskRadius = null,
initialMaskRadius = null,
imageObj = [],
imageSrcs = [],
imageWidth = null,
imageHeight = null,
mouseEvent = null;
init();
function init(el, index)
$('.dday').each(function(index)
defineCurrentElement(this, index);
positionMask();
);
function defineCurrentElement(el, index)
// Redefine the current Element
svg = $(el).closest('.dday'),
svgWidth = svg.width(),
svgHeight = svg.height(),
mask = svg.find('circle')[0];
// On page load there is a index provided to load the images for each element
if(typeof index !== 'undefined')
loadImage(index);
function loadImage(index)
// Load images and scale them to fit the predefined area
imageSrcs[index] = svg.data('image');
imageObj[index] = new Image(),
imageObj[index].image = $('image')[index];
imageObj[index].onload = function()
scale_width = maxImageWidth / this.width;
scale_height = maxImageHeight / this.height;
scale = Math.min(scale_width, scale_height);
imageWidth = this.width * scale;
imageHeight = this.height * scale;
var xCoordinate = (svgWidth - imageWidth) / 2,
yCoordinate = (svgHeight - imageHeight) / 2;
this.image.setAttributeNS('http://www.w3.org/1999/xlink','href', imageSrcs[index]);
this.image.setAttributeNS(null,'width', imageWidth);
this.image.setAttributeNS(null,'height', imageHeight);
this.image.setAttributeNS(null,'x', xCoordinate);
this.image.setAttributeNS(null,'y', yCoordinate);
;
imageObj[index].src = imageSrcs[index];
function initAnimation(ev)
// Triggered on mouseover/-out
// Change current element and init animation
defineCurrentElement(ev.target);
mouseEvent = ev.type;
requestAnimationFrame(animate);
function animate()
if(mouseEvent == 'mouseover')
// Increase mask radius on mouseover and repeat until target state is reached
maskRadius += Math.round(Math.max(((maxMaskRadius-maskRadius)/ease) * speed, 0.5));
if(maskRadius >= maxMaskRadius)
// Target radius has been reached
maskRadius = maxMaskRadius;
else
// Target radius hasn't been reached yet -> repeat animation
mask.setAttributeNS(null,'r', maskRadius);
requestAnimationFrame(animate);
else
// Decrease mask radius on mouseover and repeat until initial state is reached
maskRadius -= Math.max(((maskRadius-initialMaskRadius)/ease) * speed, 0.5);
if(maskRadius <= initialMaskRadius)
// Target radius has been reached
maskRadius = initialMaskRadius;
else
// Target radius hasn't been reached yet -> repeat animation
mask.setAttributeNS(null,'r', maskRadius);
requestAnimationFrame(animate);
function positionMask()
// Center mask inside element
maskRadius = initialMaskRadius = parseInt(mask.getAttributeNS(null, 'r'), 10);
var maskWidth = maskRadius * 2,
xCoordinate = (svgWidth - maskWidth) / 2 + maskRadius,
yCoordinate = (svgHeight - maskWidth) / 2 + maskRadius;
mask.setAttributeNS(null,'cx', xCoordinate);
mask.setAttributeNS(null,'cy', yCoordinate);
【问题讨论】:
无法打开 DEMO :( @VivekParekh 您使用哪种浏览器?我觉得jsfiddle支持IE9+,希望大家不要使用以下任何版本! 我使用 chrome。控制台显示错误。页面一直在加载。 @VivekParekh 我已经用相关代码更新了我的问题.. 你的代码很糟糕。但我正在尝试重构您的代码以使用任意数量的 【参考方案1】:好的,我修复了您的所有代码,而使用您的代码并非易事。请将你使用的所有变量声明为你自己的私有变量,不要在函数中使用全局变量,因为你可以重写现有的全局变量。
现在关于固定代码:
CSS:没有变化。HTML:删除了内联处理程序(onmouseover
和 onmouseout
)
svg
元素和类 dday
正在初始化:如果变量 svg
存在(加载逻辑几乎没有改变),则为该 svg
下载图像(加载逻辑几乎没有改变),然后居中掩码,声明函数动画,然后在初始化svg
元素时在a
元素中添加rect
元素的处理程序。
所有设置已导出到变量settings
svg
的所有私有变量都存储在 svgElement.svgData
对象中。
演示:jsFiddle
附:顺便说一句,这段代码也不是很好,需要更多时间来清理代码,但这段代码可以工作。
HTML:
<svg class="dday sector-sports normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="http://img1.wikia.nocookie.net/__cb20130511205806/epicrapbattlesofhistory/images/9/94/Vaderrotj.jpg">
<image />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" style="mask: url(#mask1)" />
</a>
<mask id="mask1">
<rect x="0" y="0" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
<svg class="dday sector-sports normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="http://static.comicvine.com/uploads/original/11111/111116692/3213841-7948839370-yoda..jpg">
<image />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" style="mask: url(#mask2)" />
</a>
<mask id="mask2">
<rect x="0" y="0" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
Javascript:(使用 jQuery 1.11 库)
$(document).ready(function ()
var settings =
imageWidthMax: 250,
imageHeightMax: 196,
ease: 50,
speed: 12,
maskRadiusMax: 100
;
var maskElements = [];
$('svg.dday').each(function (index)
if (maskElements.indexOf(this) < 0)
maskElements.push(this);
var sd = ;
this.svgData = sd;
sd.svg = $(this);
sd.svgWidth = sd.svg.width();
sd.svgHeight = sd.svg.height();
sd.mask = sd.svg.find('circle')[0];
// On page load there is a index provided to load the images for each element
if (typeof index !== 'undefined')
var img = new Image();
img.image = $('image')[index];
img.onload = function ()
var m_scale_width = settings.imageWidthMax / this.width;
var m_scale_height = settings.imageHeightMax / this.height;
var m_scale = Math.min(m_scale_width, m_scale_height);
sd.imgWidth = this.width * m_scale;
sd.imgHeight = this.height * m_scale;
var m_x = (sd.svgWidth - sd.imgWidth) / 2;
var m_y = (sd.svgHeight - sd.imgHeight) / 2;
this.image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', sd.svg.data('image'));
this.image.setAttributeNS(null, 'width', sd.imgWidth);
this.image.setAttributeNS(null, 'height', sd.imgHeight);
this.image.setAttributeNS(null, 'x', m_x);
this.image.setAttributeNS(null, 'y', m_y);
;
img.src = sd.svg.data('image');
//Center mask inside element
sd.maskRadiusInit = parseInt(sd.mask.getAttributeNS(null, 'r'), 10);
sd.maskRadius = sd.maskRadiusInit;
sd.maskWidth = sd.maskRadius * 2;
sd.maskX = (sd.svgWidth - sd.maskWidth) / 2 + sd.maskRadius;
sd.maskY = (sd.svgHeight - sd.maskWidth) / 2 + sd.maskRadius;
sd.mask.setAttributeNS(null, 'cx', sd.maskX);
sd.mask.setAttributeNS(null, 'cy', sd.maskY);
var animate = function ()
var m_addToRadius = Math.round(Math.max(((settings.maskRadiusMax - sd.maskRadius) / settings.ease) * settings.speed, 0.5));
if (sd.eventType === 'mouseover')
sd.maskRadius += m_addToRadius;
if (sd.maskRadius > settings.maskRadiusMax)
sd.maskRadius = settings.maskRadiusMax;
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
else
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
requestAnimationFrame(animate);
else
sd.maskRadius -= Math.round(Math.max(m_addToRadius, 0.5));
if (sd.maskRadius <= sd.maskRadiusInit)
sd.maskRadius = sd.maskRadiusInit;
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
else
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
requestAnimationFrame(animate);
;
$('a>rect', this).on('mouseover mouseleave', function (evt)
sd.eventType = evt.type;
requestAnimationFrame(animate);
);
);
);
演示:jsFiddle
【讨论】:
非常感谢您的努力!即使有几个错误,我也会接受这个答案(例如,在 mouseout 上它永远不会动画回真实的原始半径)。 添加sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
解决了上述问题...
@enyce12,将该行添加到 awnser 和演示中。感谢您接受我的回答。【参考方案2】:
下面是一个 Javascript 动画对象的示例,AnimateJS
它可以跨浏览器工作。
该示例显示了 svg 元素或包含 <g>
的元素的二次悬停效果。试试你的应用吧。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example: Hover Over Element - Quadratic</title>
<script type="text/javascript" src="../bowser.js"></script>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Example: Hover Over Element - Quadratic</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
When the cursor moves over the element its size is increased. This works on both transfomed and non-transformed elements, contained in a <g>, or as individual elements. Uses <b>getBBox</b> to determine scale reference point.
The previous hover size increase is reduced to its original size.
</div>
<table><tr>
<td>
<table>
<tr><td colspan=2><b>Animation Settings:</b></td></tr>
<tr><td>1. Smoothness</td><td>100 frames per second</td></tr>
<tr><td>2. Duration</td><td>200 - runtime in ms</td></tr>
<tr><td>3. Range</td><td> increase scale .5</td></tr>
<tr><td>4. Output Equation</td><td><span style=color:blue>function</span> quad(p)return Math.pow(p, 2)</td></tr>
<tr><td>6. Application Output </td><td>element transform</td></tr>
</table><br />
<i>There are 2 hover functions: <b>hoverOverG(evt)</b> for <g> elements,<br />and <b>hoverOverE(evt)</b> for individual elements.</i>
</td>
<td>
<div id="svgDiv" style='background-color:lightgreen;'>
<svg version="1.1" id="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" overflow="hidden" >
<g id="CircleStar1" onmouseover="hoverOverG(evt)" transform="translate(100 100)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id="CircleStar2" onmouseover="hoverOverG(evt)" transform="translate(200 200)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id="CircleStar3" onmouseover="hoverOverG(evt)" transform="translate(300 300)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id=rectEllipse transform="translate(330 20)scale(.5)" onmouseover="hoverOverG(evt)">
<rect x=50 y=200 width=60 height=50 fill=orange />
<ellipse cx=80 cy=227 rx=25 ry=15 fill=blue />
</g>
<g id=rectEllipseTransform transform="translate(130 120)scale(.5)" onmouseover="hoverOverG(evt)">
<rect x=50 y=200 width=60 height=50 fill=orange />
<ellipse cx=80 cy=227 rx=25 ry=15 fill=blue />
</g>
<g id=hoverElements >
<circle onmouseover="hoverOverE(evt)" cx=250 cy=150 r=10 fill=blue />
<circle onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=blue />
<circle onmouseover="hoverOverE(evt)" cx=350 cy=350 r=10 fill=blue />
<circle transform="translate(110 40)" onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=maroon />
<circle transform="translate(220 80)" onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=maroon />
<circle transform="translate(220 80)" onmouseover="hoverOverE(evt)" cx=-10 cy=-10 r=10 fill=red />
<circle transform="translate(80 320)scale(.8)" onmouseover="hoverOverE(evt)" cx=-10 cy=-10 r=10 fill=red />
</g>
</svg>
</div>
</td>
</tr> </table>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:5px;position:absolute;top:5px;left:5px;background-color:gainsboro;'>OK in:IE11/CH32/FF23<br /></div>
<script id=myScript>
/*---generalized animate core function
Allows progress/output to follow a specific/customized equation(delta)
Inspired by: Ilya Kantor - http://javascript.info/tutorial/animation
*/
var AnimateJS=function(options)
this.options=options
var start = new Date
var iT = setInterval(
function()
var timePassed = new Date - start
var progress = timePassed / options.duration
if (progress > 1) progress = 1
this.progress=progress
var delta = options.delta(progress)
options.output(delta)
if (progress == 1)clearInterval(iT);
,options.delay)
/*
provide options:
1) range(end value)
2) frames per second(delay = 1000/frames per second)
3) duration in ms
4) delta: equation(linear,etc.)
5) output: This application's output function
*/
var HoverSizeIncrease=.5 //---the element's size increased by 50%--
var FinishedOver=true
var StartTrans=null
var PrevTarget=null
//--onmouseover g symbol---
function hoverOverG(evt)
if(FinishedOver==true && (evt.target.parentNode!=PrevTarget)) //--allows initial run---
if(PrevTarget)
extractHover(PrevTarget)
var target=evt.target.parentNode
PrevTarget=target
FinishedOver=false
var scaleBegin=1
var range=HoverSizeIncrease //---scale increase
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p)return Math.pow(p,2)
if(target.getAttribute("transform"))
StartTrans=target.getAttribute("transform")
var myTrans=StartTrans
else
StartTrans=null
var myTrans=""
var bb=target.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
var scale=scaleBegin+delta*range
target.setAttribute("transform",myTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
//---finished---
if(progress==1)
onFinish()
)
//--onmouseover element---
function hoverOverE(evt)
if(FinishedOver==true && (evt.target!=PrevTarget)) //--allows initial run---
if(PrevTarget)
extractHover(PrevTarget)
var target=evt.target
PrevTarget=target
FinishedOver=false
var scaleBegin=1
var range=HoverSizeIncrease //---scale increase
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p)return Math.pow(p,2)
if(target.getAttribute("transform"))
StartTrans=target.getAttribute("transform")
var myTrans=StartTrans
else
StartTrans=null
var myTrans=""
var bb=target.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
var scale=scaleBegin+delta*range
target.setAttribute("transform",myTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
//---finished---
if(progress==1)
onFinish()
)
var FinishedExtract=true
var ExtractTarget
var ExtractTrans
function extractHover(PrevTarget)
if(FinishedExtract==true) //--allows initial run---
ExtractTarget=PrevTarget
if(StartTrans)
ExtractTrans=StartTrans
else
ExtractTrans=""
FinishedExtract=false
var scaleBegin=1+HoverSizeIncrease
var range=HoverSizeIncrease //---scale decrease
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p)return Math.pow(p,2)
var bb=ExtractTarget.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
var scale=scaleBegin-delta*range
ExtractTarget.setAttribute("transform",ExtractTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
if (progress == 1) // --- finished---
extractFinish();
)
//---this example animation: loop finished---
function onFinish()
FinishedOver=true
//---this example animation: loop finished---
function extractFinish()
FinishedExtract=true
if(ExtractTrans!="")
ExtractTarget.setAttribute("transform",ExtractTrans)
else
ExtractTarget.removeAttribute("transform")
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
jsValue.value=myScript.text
svgSourceValue.value=svgDiv.innerHTML
</script>
</body>
</html>
【讨论】:
【参考方案3】:尝试使用非常简单的方法来做到这一点。我没有应用任何动画。我想你会想办法做到这一点的。
从内联中删除鼠标事件
JS
$('svg')
.mouseenter(function(ev)
console.log($(this).find('circle').attr('r',40));
)
.mouseleave(function(ev)
console.log($(this).find('circle').attr('r',25));
);
检查JSFiddle
【讨论】:
以上是关于带有多个元素的 SVG 悬停的主要内容,如果未能解决你的问题,请参考以下文章