Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

offsets are from the center of element instead of from the top-left corner #789

Closed
2 tasks
srkannan opened this issue Jun 19, 2017 · 18 comments
Closed
2 tasks

Comments

@srkannan
Copy link

In order to help us efficiently investigate your issue, please provide the following information:

Platform and application details

  • Platform: macOS Sierra 10.12.5
  • Firefox: Firefox 54
  • Selenium: 3.4.0

Steps to reproduce

In org.openqa.selenium.interactions.Actions the x, y offsets for moveToElement has been Offset from the top-left corner.
But with geckodriver I'm getting:
Jun 16, 2017 5:55:05 PM org.openqa.selenium.interactions.Actions moveToElement
INFO: When using the W3C Action commands, offsets are from the center of element

Has the offset changed to center of element instead of top-left corner? While chromedriver, old firefox driver and safaridriver calculates the offset from top-left corner is this a bug in geckodriver?

  • Reproducable testcase:
    Try moveToElement method in org.openqa.selenium.interactions.Actions
    It differs between old firefox drivers.

  • A trace level log:
    Jun 16, 2017 5:55:05 PM org.openqa.selenium.interactions.Actions moveToElement
    INFO: When using the W3C Action commands, offsets are from the center of element

@andreastt
Copy link
Contributor

moveToElement is a shim provided by Selenium so it’s possible there is a bug with that. But without a reproducible test case and a trace-level geckodriver log, it is hard to which level this fails at. Can you please provide that?

@srkannan
Copy link
Author

The problem is the x,y offsets I used for old firefox driver and chromedriver don't work for geckodriver. So I get 'indexoutofbounds' exception when using the x,y offsets that I used for earlier versions of the driver. I will try to come up with a reproducible generic test case.

@andreastt
Copy link
Contributor

Sure, but please note that neither FirefoxDriver (from Selenium) or chromedriver are implementations of the WebDriver specification. It would be useful to see a trace-level log.

@srkannan
Copy link
Author

Here is a simple code to demonstrate the problem. The actual application I automate needs drawing that is why I rely on the Action.moveToElement but I just picked this one to demo the problem.

System.setProperty("webdriver.gecko.driver", "/Users/Downloads/geckodriver");
System.setProperty("webdriver.chrome.driver", "/Users/Downloads/chromedriver");
WebDriver driver = new FirefoxDriver();
// WebDriver driver = new ChromeDriver();
driver.get("http://book.theautomatedtester.co.uk/");
Actions action = new Actions(driver);
WebElement mainbody = driver.findElement(By.xpath("html/body/div[2]/ul/li[1]"));
action.moveToElement(mainbody,10,10);
action.click().build().perform();

Please run this code using geckdriver and then comment out the FirefoxDriver and use the chromedriver line by uncommenting. Notice that the moveToElement works fine for chrome(it worked in old firefox driver as well) but in geckodriver it fails as the offset is computed from the center of the element instead of from top-left corner.

@andreastt
Copy link
Contributor

I don’t know what action.moveToElement does in the Selenium client, which is why I asked to see the geckodriver trace log so I can see what primitives are actually sent across the wire to geckodriver.

That it “worked before” is not a great bug.

@srkannan
Copy link
Author

srkannan commented Jun 20, 2017

Below is the log:

1497979442395	geckodriver	INFO	Listening on 127.0.0.1:44950
1497979446968	geckodriver::marionette	INFO	Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin with args ["-marionette"]
1497979453319	Marionette	INFO	Listening on port 59254
1497979454432	Marionette	DEBUG	loaded listener.js
2017-06-20 10:24:14.896 plugin-container[90592:1542522] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0x9c3b, name = 'com.apple.tsm.portname'
See /usr/include/servers/bootstrap_defs.h for the error codes.
2017-06-20 10:24:14.897 plugin-container[90592:1542522] *** CFMessagePort: bootstrap_register(): failed 1100 (0x44c) 'Permission denied', port = 0x9f03, name = 'com.apple.CFPasteboardClient'
See /usr/include/servers/bootstrap_defs.h for the error codes.
Jun 20, 2017 10:24:14 AM org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C
1497979455174	Marionette	DEBUG	Received DOM event "beforeunload" for "about:blank"
1497979455350	Marionette	DEBUG	Received DOM event "pagehide" for "about:blank"
1497979455350	Marionette	DEBUG	Received DOM event "unload" for "about:blank"
1497979455782	Marionette	DEBUG	Received DOM event "DOMContentLoaded" for "http://book.theautomatedtester.co.uk/"
1497979455785	Marionette	DEBUG	Received DOM event "pageshow" for "http://book.theautomatedtester.co.uk/"
Jun 20, 2017 10:24:15 AM org.openqa.selenium.interactions.Actions moveToElement
INFO: When using the W3C Action commands, offsets are from the center of element

@florentbr
Copy link

florentbr commented Jun 21, 2017

With the old protocol (Wire) the command to move the cursor was moveto and the provided offset was relative to the top-left corner of the targeted element:

https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidmoveto

But with the new W3C specification and implementation in marionette, moving the cursor to an element now requires a composite action where the offset is relative to the element's center:

https://w3c.github.io/webdriver/webdriver-spec.html#dfn-dispatch-a-pointermove-action

An object that represents a web element
Let element be equal to the result of trying to get a known element with argument origin.

Let x element and y element be the result of calculating the in-view center point of element.

Let x equal x element + x offset, and y equal y element + y offset.

It seems that the specification was updated to accommodate the implementation of marionette which was already relative to center even before Firefox 45.

So this behavior is by design.

The current api doesn't provide a way to directly move the cursor with an offset relative to the top-left corner. It would make much more sense to provide an offset relative to the top-left corner especially when testing canvas.

That said, I would still consider this an issue, first because it breaks a previous feature and second because this change doesn't bring any value to the api.

@andreastt
Copy link
Contributor

The current api doesn't provide a way to directly move the cursor with an offset relative to the top-left corner. It would make much more sense to provide an offset relative to the top-left corner especially when testing canvas.

I think this is a fair point. @florentbr, do you want to raise an issue against the specification?

cc @AutomatedTester

@barancev
Copy link

I can add some info based on Selenium Java test suite, there is a test against this page: https://github.com/SeleniumHQ/selenium/blob/master/common/src/web/mousePositionTracker.html

  public void testMovingMouseToRelativeElementOffset() {
    driver.get(pages.mouseTrackerPage);

    WebElement trackerDiv = driver.findElement(By.id("mousetracker"));
    new Actions(driver).moveToElement(trackerDiv, 95, 195).perform();

    WebElement reporter = driver.findElement(By.id("status"));

    wait.until(fuzzyMatchingOfCoordinates(reporter, 95, 195));
  }

The result is "78, 191" instead of expected "95, 195"

Trace log:

1503771497876	geckodriver	INFO	geckodriver 0.18.0
1503771497877	webdriver::httpapi	DEBUG	Creating routes
1503771497887	geckodriver	INFO	Listening on 127.0.0.1:36706
1503771498529	webdriver::server	DEBUG	-> POST /session {
  "desiredCapabilities": {
    "nativeEvents": false,
    "firefox_binary": {
      "extraEnv": {},
      "hCode": 2008966511,
      "class": "org.openqa.selenium.firefox.FirefoxBinary",
      "timeout": 45000
    },
    "marionette": true,
    "acceptInsecureCerts": true,
    "browserName": "firefox",
    "moz:firefoxOptions": {
      "binary": "c:\\Program Files\\Nightly\\firefox.exe",
      "prefs": {},
      "args": []
    },
    "platformName": "ANY",
    "version": "",
    "platform": "ANY"
  },
  "requiredCapabilities": {},
  "capabilities": {
    "desiredCapabilities": {
      "nativeEvents": false,
      "firefox_binary": {
        "extraEnv": {},
        "hCode": 2008966511,
        "class": "org.openqa.selenium.firefox.FirefoxBinary",
        "timeout": 45000
      },
      "marionette": true,
      "acceptInsecureCerts": true,
      "browserName": "firefox",
      "moz:firefoxOptions": {
        "binary": "c:\\Program Files\\Nightly\\firefox.exe",
        "prefs": {},
        "args": []
      },
      "platformName": "ANY",
      "version": "",
      "platform": "ANY"
    },
    "requiredCapabilities": {},
    "alwaysMatch": {
      "acceptInsecureCerts": true
    },
    "firstMatch": [
      {
        "browserName": "firefox",
        "moz:firefoxOptions": {
          "binary": "c:\\Program Files\\Nightly\\firefox.exe",
          "prefs": {},
          "args": []
        }
      }
    ]
  }
}
1503771498547	geckodriver::capabilities	DEBUG	Trying to read firefox version from ini files
1503771498548	geckodriver::capabilities	DEBUG	Found version 57.0a1
1503771498560	geckodriver::marionette	INFO	Starting browser c:\Program Files\Nightly\firefox.exe with args ["-marionette"]
1503771498918	addons.xpi	WARN	Error parsing extensions state: [Exception... "Component returned failure code: 0x80520012 (NS_ERROR_FILE_NOT_FOUND) [amIAddonManagerStartup.readStartupData]"  nsresult: "0x80520012 (NS_ERROR_FILE_NOT_FOUND)"  location: "JS frame :: resource://gre/modules/addons/XPIProvider.jsm :: loadExtensionState :: line 1523"  data: no] Stack trace: loadExtensionState()@resource://gre/modules/addons/XPIProvider.jsm:1523 < getInstallState()@resource://gre/modules/addons/XPIProvider.jsm:1558 < checkForChanges()@resource://gre/modules/addons/XPIProvider.jsm:3093 < startup()@resource://gre/modules/addons/XPIProvider.jsm:2160 < callProvider()@resource://gre/modules/AddonManager.jsm:263 < _startProvider()@resource://gre/modules/AddonManager.jsm:733 < startup()@resource://gre/modules/AddonManager.jsm:900 < startup()@resource://gre/modules/AddonManager.jsm:3084 < observe()@jar:file:///C:/Program%20Files/Nightly/omni.ja!/components/addonManager.js:65
1503771499180	Marionette	DEBUG	Received observer notification "profile-after-change"
1503771499273	Marionette	DEBUG	Received observer notification "command-line-startup"
1503771499273	Marionette	INFO	Enabled via --marionette
1503771499564	geckodriver::marionette	TRACE	  connection attempt 0/600
1503771500664	geckodriver::marionette	TRACE	  connection attempt 1/600
Unable to read VR Path Registry from C:\Users\alexei\AppData\Local\openvr\openvrpaths.vrpath
[Child 5292] WARNING: pipe error: 109: file z:/build/build/src/ipc/chromium/src/chrome/common/ipc_channel_win.cc, line 346
1503771501526	Marionette	DEBUG	Received observer notification "sessionstore-windows-restored"
1503771501765	geckodriver::marionette	TRACE	  connection attempt 2/600
1503771502122	Marionette	DEBUG	Setting recommended pref toolkit.cosmeticAnimations.enabled to false
1503771502122	Marionette	DEBUG	Setting recommended pref datareporting.policy.dataSubmissionPolicyAccepted to false
1503771502122	Marionette	DEBUG	Setting recommended pref dom.file.createInChild to true
1503771502123	Marionette	INFO	Listening on port 63806
1503771502372	geckodriver::marionette	DEBUG	Connected to Marionette on localhost:63806
1503771502375	Marionette	DEBUG	Accepted connection 0 from 127.0.0.1:63821
1503771502375	geckodriver::marionette	TRACE	<- {"applicationType":"gecko","marionetteProtocol":3}
1503771502375	geckodriver::marionette	TRACE	-> 163:[0,1,"newSession",{"acceptInsecureCerts":true,"browserName":"firefox","capabilities":{"desiredCapabilities":{"acceptInsecureCerts":true,"browserName":"firefox"}}}]
1503771502376	Marionette	TRACE	0 -> [0,1,"newSession",{"acceptInsecureCerts":true,"browserName":"firefox","capabilities":{"desiredCapabilities":{"acceptInsecureCerts":true,"browserName":"firefox"}}}]
1503771502378	Marionette	WARN	TLS certificate errors will be ignored for this session
1503771502427	Marionette	DEBUG	Register listener.js for window 4294967297
1503771502446	Marionette	TRACE	0 <- [1,1,null,{"sessionId":"c716d576-e364-4122-9a4d-1962333ff7dc","capabilities":{"browserName":"firefox","browserVersion":"57.0a1","platformName":"windows_nt","platformVersion":"6.1","pageLoadStrategy":"normal","acceptInsecureCerts":true,"timeouts":{"implicit":0,"pageLoad":300000,"script":30000},"rotatable":false,"specificationLevel":0,"moz:processID":9276,"moz:profile":"C:\\Users\\alexei\\AppData\\Local\\Temp\\rust_mozprofile.KmpLScKEztfq","moz:accessibilityChecks":false,"moz:headless":false}}]
1503771502447	geckodriver::marionette	TRACE	<- [1,1,null,{"sessionId":"c716d576-e364-4122-9a4d-1962333ff7dc","capabilities":{"browserName":"firefox","browserVersion":"57.0a1","platformName":"windows_nt","platformVersion":"6.1","pageLoadStrategy":"normal","acceptInsecureCerts":true,"timeouts":{"implicit":0,"pageLoad":300000,"script":30000},"rotatable":false,"specificationLevel":0,"moz:processID":9276,"moz:profile":"C:\\Users\\alexei\\AppData\\Local\\Temp\\rust_mozprofile.KmpLScKEztfq","moz:accessibilityChecks":false,"moz:headless":false}}]
1503771502447	webdriver::server	DEBUG	<- 200 OK {"value": {"sessionId":"c716d576-e364-4122-9a4d-1962333ff7dc","capabilities":{"acceptInsecureCerts":true,"browserName":"firefox","browserVersion":"57.0a1","moz:accessibilityChecks":false,"moz:headless":false,"moz:processID":9276,"moz:profile":"C:\\Users\\alexei\\AppData\\Local\\Temp\\rust_mozprofile.KmpLScKEztfq","pageLoadStrategy":"normal","platformName":"windows_nt","platformVersion":"6.1","rotatable":false,"specificationLevel":0,"timeouts":{"implicit":0,"pageLoad":300000,"script":30000}}}}
1503771502803	webdriver::server	DEBUG	-> POST /session/c716d576-e364-4122-9a4d-1962333ff7dc/url {"url":"http://comp1:43833/common/mousePositionTracker.html"}
1503771502803	geckodriver::marionette	TRACE	-> 73:[0,2,"get",{"url":"http://comp1:43833/common/mousePositionTracker.html"}]
1503771502805	Marionette	TRACE	0 -> [0,2,"get",{"url":"http://comp1:43833/common/mousePositionTracker.html"}]
1503771502811	Marionette	DEBUG	Received DOM event "beforeunload" for "about:blank"
1503771502883	Marionette	DEBUG	Received DOM event "pagehide" for "about:blank"
1503771502883	Marionette	DEBUG	Received DOM event "unload" for "about:blank"
1503771502969	Marionette	DEBUG	Received DOM event "DOMContentLoaded" for "http://comp1:43833/common/mousePositionTracker.html"
1503771502971	Marionette	DEBUG	Received DOM event "pageshow" for "http://comp1:43833/common/mousePositionTracker.html"
1503771502981	Marionette	TRACE	0 <- [1,2,null,{}]
1503771502997	geckodriver::marionette	TRACE	<- [1,2,null,{}]
1503771502997	webdriver::server	DEBUG	<- 200 OK {"value": {}}
1503771503004	webdriver::server	DEBUG	-> POST /session/c716d576-e364-4122-9a4d-1962333ff7dc/element {"value":"#mousetracker","using":"css selector"}
1503771503004	geckodriver::marionette	TRACE	-> 68:[0,3,"findElement",{"using":"css selector","value":"#mousetracker"}]
1503771503015	Marionette	TRACE	0 -> [0,3,"findElement",{"using":"css selector","value":"#mousetracker"}]
1503771503019	Marionette	TRACE	0 <- [1,3,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88","ELEMENT":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"}}]
1503771503020	geckodriver::marionette	TRACE	<- [1,3,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88","ELEMENT":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"}}]
1503771503020	webdriver::server	DEBUG	<- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"}}
1503771503035	webdriver::server	DEBUG	-> POST /session/c716d576-e364-4122-9a4d-1962333ff7dc/actions {"actions":[{"id":"default mouse","type":"pointer","parameters":{"pointerType":"mouse"},"actions":[{"duration":100,"x":95,"y":195,"type":"pointerMove","origin":{"ELEMENT":"3d0d6af0-087b-462a-bd96-03fcb57b3a88","element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"}}]}]}
1503771503036	geckodriver::marionette	TRACE	-> 266:[0,4,"performActions",{"actions":[{"actions":[{"duration":100,"origin":{"element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"},"type":"pointerMove","x":95,"y":195}],"id":"default mouse","parameters":{"pointerType":"mouse"},"type":"pointer"}]}]
1503771503046	Marionette	TRACE	0 -> [0,4,"performActions",{"actions":[{"actions":[{"duration":100,"origin":{"element-6066-11e4-a52e-4f735466cecf":"3d0d6af0-087b-462a-bd96-03fcb57b3a88"},"type":"pointerMove","x":95,"y":195}],"id":"default mouse","parameters":{"pointerType":"mouse"},"type":"pointer"}]}]
1503771503171	Marionette	TRACE	0 <- [1,4,null,{}]
1503771503171	geckodriver::marionette	TRACE	<- [1,4,null,{}]
1503771503171	webdriver::server	DEBUG	<- 200 OK {"value": {}}
1503771503174	webdriver::server	DEBUG	-> POST /session/c716d576-e364-4122-9a4d-1962333ff7dc/element {"value":"#status","using":"css selector"}
1503771503174	geckodriver::marionette	TRACE	-> 62:[0,5,"findElement",{"using":"css selector","value":"#status"}]
1503771503176	Marionette	TRACE	0 -> [0,5,"findElement",{"using":"css selector","value":"#status"}]
1503771503178	Marionette	TRACE	0 <- [1,5,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"d4586469-bad5-4bb7-8b65-d3d2d5c38421","ELEMENT":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}}]
1503771503178	geckodriver::marionette	TRACE	<- [1,5,null,{"value":{"element-6066-11e4-a52e-4f735466cecf":"d4586469-bad5-4bb7-8b65-d3d2d5c38421","ELEMENT":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}}]
1503771503178	webdriver::server	DEBUG	<- 200 OK {"value":{"element-6066-11e4-a52e-4f735466cecf":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}}
1503771503182	webdriver::server	DEBUG	-> GET /session/c716d576-e364-4122-9a4d-1962333ff7dc/element/d4586469-bad5-4bb7-8b65-d3d2d5c38421/text 
1503771503183	geckodriver::marionette	TRACE	-> 68:[0,6,"getElementText",{"id":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}]
1503771503184	Marionette	TRACE	0 -> [0,6,"getElementText",{"id":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}]
1503771503193	Marionette	TRACE	0 <- [1,6,null,{"value":"78, 191"}]
1503771503193	geckodriver::marionette	TRACE	<- [1,6,null,{"value":"78, 191"}]
1503771503193	webdriver::server	DEBUG	<- 200 OK {"value":"78, 191"}
1503771503698	webdriver::server	DEBUG	-> GET /session/c716d576-e364-4122-9a4d-1962333ff7dc/element/d4586469-bad5-4bb7-8b65-d3d2d5c38421/text 
1503771503698	geckodriver::marionette	TRACE	-> 68:[0,7,"getElementText",{"id":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}]
1503771503699	Marionette	TRACE	0 -> [0,7,"getElementText",{"id":"d4586469-bad5-4bb7-8b65-d3d2d5c38421"}]
1503771503704	Marionette	TRACE	0 <- [1,7,null,{"value":"78, 191"}]
1503771503704	geckodriver::marionette	TRACE	<- [1,7,null,{"value":"78, 191"}]
1503771503704	webdriver::server	DEBUG	<- 200 OK {"value":"78, 191"}

@Debanjan-B

This comment has been minimized.

@whimboo
Copy link
Collaborator

whimboo commented Aug 23, 2018

@andreastt based on your comment from July last year (#789 (comment)) it looks like that no spec issue has been filed, or?

@raguravindran
Copy link

raguravindran commented Mar 20, 2019

I do not see any changes from FF or on geckodriver. I am using geckodriver 24 with FF 65.0.2
I see the same behaviour that it takes centre of any given element. We did an implementation to take the offset and click top-left corner.

I feel the solution to this problem has been left hanging in lot of places where I see similar questions.
I recently had this problem with geckodriver 19 and FF 57. We also upgraded to FF 65 and gecko being 24. So, gecko is still continuing to have W3C specs intact but not the same with chromedriver (or not?).

We resolved this situation by,

WebElement ele = findElement(By.xpath("//div[contains(@class, 'columnClass')]"));

// Action to move to the element top-left corner
Actions userAction = new Actions(getDriver());
int getTopLeftY = ((ele.getSize().getHeight()/2) - ele.getSize().getHeight());
int getTopLeftX =  (ele.getSize().getWidth()/2) - ele.getSize().getWidth();
userAction.moveToElement(ele, getTopLeftX, getTopLeftY).click().perform();

From this if you want to move to any point at the element. you may want to

int positionX = getTopLeftX + 100;
int positionY = getTopLeftY + 100;

Which will click at the point where you are anticipating.

@whimboo
Copy link
Collaborator

whimboo commented Mar 27, 2019

@raguravindran I assume you didn't run chromedriver in w3c mode? It should fail the same way.

Also please read the documentation of geckodriver and use the following capability for now:

https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html#moz-usenonspeccompliantpointerorigin

@JoolsCaesar
Copy link

The bug is in selenium. The drivers for Chrome and Edge Dev (chromium), since version 75, both comply with the w3c. Selenium need to update their javadocs to say that it's from the centre. It would probably be too much of a faff to fix it in the selenium code because they won't know the dimensions of the element.

@andreastt
Copy link
Contributor

There is precedence for high-level Selenium client commands to run extra steps to alter the behaviour of commands. WebElement#submit() is one such example, as Selenium offered a way to submit a form but WebDriver (by the standard) doesn’t.

So it’s technically possible to mitigate the difference in behaviour for Selenium users by the clients first getting the bounding box of the element and then changing the click point using an action primitive. That said, if recent Chrome and Edge drivers are aligned with geckodriver’s behaviour perhaps it is not a priority.

@mixalbl4-127
Copy link

mixalbl4-127 commented Jan 31, 2022

int getTopLeftY = ((ele.getSize().getHeight()/2) - ele.getSize().getHeight());
int getTopLeftX = (ele.getSize().getWidth()/2) - ele.getSize().getWidth();

height/2 is wrong because of oveflow: scroll in parent and element height bigger than screen height (when a part of element hidden)

@whimboo
Copy link
Collaborator

whimboo commented May 2, 2023

Is anyone still having issues with this particular behavior? If yes please report and provide a testcase + trace log. Otherwise I'm going to close this issue soon. Thanks.

@whimboo
Copy link
Collaborator

whimboo commented May 8, 2023

Due to lack of response I'm going to close this issue for now. I'm happy to reopen if there are still use cases out there which do not work with the center of element approach.

@whimboo whimboo closed this as completed May 8, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants